Interface Builder — egy vizuális felületszerkesztő, amely az Xcode-ba van beépítve iOS és macOS fejlesztéshez. Lehetővé teszi UI létrehozását drag-and-drop segítségével, Auto Layout beállítását, kód csatlakoztatását IBOutlet és IBAction segítségével. Elemezzük, hogyan működik az IB, miben különbözik a Storyboard és az XIB, és miért van szükség a @IBDesignable-re.
Főbb pontok
Interface Builder — az Xcode egyik összetevője, amely felhasználói felületek vizuális tervezésére szolgál. Az IB története 1988-ban kezdődött a NeXT cégnél, jóval az iOS megjelenése előtt. Stefan Pope fejlesztette ki az első verziót a NeXTSTEP számára — az operációs rendszer számára, amely a macOS és iOS alapjává vált. 1996-ban az Apple felvásárolta a NeXT-et és integrálta az Interface Builder-t az Xcode-ba.
A modern Xcode-ban az Interface Builder három fájlformátumot támogat: Storyboard, XIB (Xcode Interface Builder) és XIB fájlokat táblázatcellákhoz és egyedi view-khoz. Ezen formátumok mindegyike XML leírást tárol az UI elemek hierarchiájáról, tulajdonságaikról, korlátozásaikról és a kóddal való kapcsolataikról.
Az IB UIKit szinten működik: gombok, címkék, szövegmezők, táblázatok, gyűjtemények és korlátozások egérrel húzhatók a vászonra. Az Xcode a .storyboard és .xib fájlokat a build fázisban nib archívumokká (compiled Interface Builder) fordítja, ami csökkenti a bundle méretét és gyorsítja a betöltést.
Az Apple adatai szerint az iOS projektek több mint 70%-a használja az Interface Builder-t a fejlesztés különböző szakaszaiban. A SwiftUI térnyerése ellenére az IB továbbra is szabvány marad az iOS 12-t és az alatti verziókat támogató kereskedelmi alkalmazások számára, valamint az összetett egyedi felületekhez, amelyek az Auto Layout finomhangolását igénylik.
Az Xcode 4 előtt az Interface Builder egy különálló alkalmazás volt, amely a kódszerkesztővel párhuzamosan futott. Az Xcode 4-ben (2011) az Apple egyesítette az IB-t és a kódszerkesztőt egyetlen IDE-be. Ez lehetővé tette a kód és az elrendezés közötti váltást ablakváltás nélkül, valamint a tulajdonságok változásainak valós idejű megtekintését a Attributes Inspector panelen keresztül.
| Xcode verzió | Év | Változások az Interface Builder-ben |
|---|---|---|
| Xcode 3 | 2008 | IB — különálló alkalmazás, iOS 2.0 támogatás |
| Xcode 4 | 2011 | IB beépítve az IDE-be, Storyboard megjelenése |
| Xcode 5 | 2013 | Auto Layout korlátozás menüvel, képernyő előnézet |
| Xcode 6 | 2014 | Size Classes, @IBDesignable, Preview Assistant |
| Xcode 11 | 2019 | SwiftUI Canvas, IB megmarad UIKit számára |
| Xcode 15 | 2023 | SwiftUI Preview fő eszközként, IB legacy mód |
A SwiftUI 2019-es megjelenésével az Apple a deklaratív fejlesztésre helyezte a hangsúlyt, azonban az Interface Builder továbbra is beépítve marad az Xcode-ba a UIKit projektek támogatására. Több ezer meglévő alkalmazás használja továbbra is az IB-t, és az Apple nem jelentette be annak eltávolítását.
Az Interface Builder két fő formátumot támogat: Storyboard (.storyboard) és XIB (.xib). A köztük lévő különbség a hatókörben és a használati forgatókönyvben rejlik.
Storyboard — az alkalmazás teljes jelenetét tartalmazó fájl: több képernyő (UIViewController), átmenetek közöttük (segues), navigációs vezérlők, tab-sávok és az összes UI elem. A Storyboard egyszer töltődik be indításkor az Info.plist-ből a UIMainStoryboardFile (k) kulcson keresztül. Kényelmes a képernyők folyamatának vizualizálásához, de problémákat okoz a merge-konfliktusoknál a git-ben, mivel a teljes alkalmazás XML leírása egyetlen fájlban tárolódik.
XIB (az Xcode Interface Builder rövidítése) — fájl egyetlen komponenshez: különálló UIView, UITableViewCell, UICollectionViewCell vagy egy ViewController. Az XIB igény szerint töltődik be a UINib(nibName:bundle:) (k) vagy a Bundle.loadNibNamed (k) metódus segítségével. Az XIB fájlokat könnyebb merge-elni, kompaktabbak és gyorsabban betölthetők, mivel nem tartalmazzák a teljes alkalmazás leírását.
| Szempont | Storyboard | XIB |
|---|---|---|
| Hatókör | Több képernyő + átmenetek | Egy képernyő vagy komponens |
| Segues | Támogatja (push, modal, unwind) | Nem támogatja |
| Merge git-ben | Bonyolult (egy nagy XML) | Egyszerű (sok kis fájl) |
| Betöltés | Alkalmazás indításakor | Igény szerint (lustán) |
| Újrafelhasználás | Csak storyboard references segítségével | Magas (cellák, fejlécek, view-k) |
| Apple ajánlás | Nem ajánlott nagy projektekhez | Komponensekhez ajánlott |
Az Xcode 11 verziótól kezdve az Apple az XIB használatát ajánlja az egyes komponensekhez, és kerülni kell a monolitikus Storyboard-okat. A képernyők közötti navigációhoz inkább a kód alapú navigációt részesítik előnyben a UIStoryboardSegue (k) manuális használatával vagy koordinátorokkal.
A .storyboard és .xib fájlok XML-t tárolnak Interface Builder Cocoa Touch XIB (dt) formátumban. Példa egyszerűsített szerkezetre:
<!-- XIB fájl UIView és UILabel elemekkel -->
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB"
version="3.0">
<objects>
<view id="abc-123"
userLabel="CustomHeaderView"
contentMode="scaleToFill">
<subviews>
<label id="def-456"
text="Cím"
textColor="darkTextColor"
fontDescription="title1"/>
</subviews>
</view>
</objects>
</document>Minden elemnek egyedi id (an) van, amely alapján az IB összekapcsolja az XML csomópontot a futásidejű objektummal. Fordításkor az Xcode az XML-t bináris nib formátumba (.nib) alakítja, körülbelül 40%-kal csökkentve a fájl méretét.
Auto Layout — az elemek képernyőn történő elhelyezésének rendszere matematikai kapcsolatok (korlátozások) segítségével. Az Interface Builder vizuális felületet biztosít korlátozások létrehozásához, szerkesztéséhez és hibakereséséhez kód írása nélkül. Minden korlátozás egy függőséget ír le: view.leading = superview.leading + 16 (k) vagy view.width = 2 * otherView.height (k).
Az IB-ben a korlátozások a Pin (margók, szélesség, magasság rögzítése) és Align (középre, élekhez, baseline igazítás) menük segítségével hozhatók létre. A Size Inspector panel megjeleníti a kiválasztott elem összes korlátozását, azok prioritásait (required/high/low), és lehetővé teszi a szorzók és konstansok szerkesztését.
Az IB támogatja az UIStackView-t is — egy tárolót, amely automatikusan kezeli a gyermek view-k elrendezését. Elég az elemeket egy stack view-ba helyezni a vásznon, és az IB automatikusan létrehozza a szükséges korlátozásokat. Ez jelentősen felgyorsítja az elrendezést a kézi korlátozás elhelyezéshez képest.
Size Classes — egy absztrakció, amely az eszközöket képernyő szélesség és magasság szerint csoportosítja: Compact és Regular. A kombinációk (wC hR iPhone portréhoz, wR hR iPadhez) lehetővé teszik különböző korlátozások és elem elrendezések beállítását különböző forgatókönyvekhez. Az Interface Builder-ben a size classes közötti váltás megváltoztatja az aktív korlátozások halmazát a vásznon.
| Eszköz | Tájolás | Width Class | Height Class |
|---|---|---|---|
| iPhone (kivéve Max/Plus) | Portré | Compact | Regular |
| iPhone (kivéve Max/Plus) | Tájkép | Compact | Compact |
| iPhone Plus/Max | Tájkép | Regular | Compact |
| iPad | Bármely | Regular | Regular |
| iPad Split View | 1/3 képernyő | Compact | Regular |
Példa korlátozásra size class szerinti változattal:
import UIKit
class AdaptiveViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var leadingConstraint: NSLayoutConstraint!
private func updateConstraints() {
let isRegular = traitCollection.horizontalSizeClass == .regular
leadingConstraint.constant = isRegular ? 40 : 16
titleLabel.font = isRegular
? UIFont.preferredFont(forTextStyle: .largeTitle)
: UIFont.preferredFont(forTextStyle: .title1)
}
override func traitCollectionDidChange(
_ previousTraitCollection: UITraitCollection?
) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {
updateConstraints()
}
}
}A fenti kódban a traitCollectionDidChange reagál a size class változására, frissítve a korlátozást és a betűtípust. Az Interface Builder lehetővé teszi alapértelmezett értékek beállítását minden size class számára az inspektoron keresztül, a kód pedig a dinamikus forgatókönyvekhez használatos, amelyek nem írhatók le statikusan.
A vizuális felület (Interface Builder) és a Swift/Objective-C kód közötti kapcsolat két mechanizmuson keresztül valósul meg: IBOutlet (Interface Builder Outlet) és IBAction (Interface Builder Action). Mindkettő az IB vászonról történő, lenyomva tartott Ctrl billentyűvel végzett húzással jön létre a vezérlő fájlba.
IBOutlet — egy annotáció, amely egy UI elemre hivatkozik. Az Xcode automatikusan csatlakoztatja a megfelelő objektumhoz a nib archívumban betöltéskor. Ha a kapcsolat megszakad (például az elem átnevezésekor), az alkalmazás NSUnknownKeyException (k) hibával összeomlik. Az IBOutlet weak (k) jelöléssel van ellátva, mivel a nib birtokolja az objektumot, a vezérlő pedig csak megfigyelő.
IBAction — egy metódus, amely egy UI elem eseményeként hívódik meg: gombnyomás, szövegváltoztatás, kapcsoló átbillentése. Az IB a UIControlEvent-et (k) kapcsolja össze a metódussal a addTarget:action:forControlEvents: (k) segítségével. A kódban az IBAction egy normál metódusként jelenik meg IBAction (dt) visszatérési típussal.
import UIKit
final class LoginViewController: UIViewController {
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var spinner: UIActivityIndicatorView!
@IBAction private func loginButtonTapped(_ sender: UIButton) {
guard let email = emailTextField.text, !email.isEmpty,
let password = passwordTextField.text, !password.isEmpty
else {
showAlert(message: "Töltse ki az összes mezőt")
return
}
loginButton.isEnabled = false
spinner.startAnimating()
performLogin(email: email, password: password)
}
private func performLogin(email: String, password: String) {
/// API hívás URLSession segítségével
let request = LoginRequest(email: email, password: password)
APIClient.shared.login(request) { [weak self] result in
DispatchQueue.main.async {
guard let self else { return }
self.spinner.stopAnimating()
self.loginButton.isEnabled = true
switch result {
case .success:
self.navigateToMainScreen()
case .failure(let error):
self.showAlert(message: error.localizedDescription)
}
}
}
}
private func showAlert(message: String) {
let alert = UIAlertController(
title: "Hiba",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}A példában egy szabványos kapcsolat látható: IBOutlet szövegmezőkhöz, gombhoz és spinnerhez, IBAction a gombnyomás kezeléséhez. Mindezek a kapcsolatok az Interface Builder-ben jönnek létre Ctrl+drag segítségével. Ha a kapcsolat nincs konfigurálva, az IBOutlet futásidőben nil (v) lesz, ami összeomlást okoz a hozzáféréskor — ezért az IBOutlet implicit unwrap-pal ellátott weak var (k s) formában deklarálódik.
@IBDesignable — egy Swift annotáció, amely lehetővé teszi egy egyedi UIView valós idejű megjelenítését közvetlenül az Interface Builder vásznán. A fejlesztő látja a kódváltoztatások eredményét az alkalmazás elindítása nélkül. @IBInspectable — egy annotáció tulajdonságokhoz, amely hozzáadja azokat az IB Attributes Inspector paneljéhez, ahol az értékek interaktívan módosíthatók.
Ezek az annotációk különösen hasznosak UI komponenskönyvtárak létrehozásakor: egyedi gombok, maszkolt beviteli mezők, animált jelzők. Az IBDesignable a prepareForInterfaceBuilder() (fn) metódust használja a build kód külön fordításához, amely nem érinti az alkalmazás fő bináris állományát.
import UIKit
@IBDesignable
final class GradientButton: UIButton {
@IBInspectable var startColor: UIColor = .systemBlue {
didSet { updateGradient() }
}
@IBInspectable var endColor: UIColor = .systemPurple {
didSet { updateGradient() }
}
@IBInspectable var cornerRadius: CGFloat = 12 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = true
}
}
private let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
setupGradient()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupGradient()
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
private func setupGradient() {
layer.insertSublayer(gradientLayer, at: 0)
updateGradient()
}
private func updateGradient() {
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupGradient()
}
}A fenti kódban a GradientButton egy IBDesignable komponens IBInspectable tulajdonságokkal: startColor (v), endColor (v) és cornerRadius (v). Amikor egy UIView-t húzunk az IB vászonra, és az Identity Inspector-ban a GradientButton osztályra változtatjuk, a vásznon valós időben megjelenik egy gradiens gomb. Az összes IBInspectable tulajdonság megjelenik a jobb oldali Attributes Inspector panelen.
Fontos: a @IBDesignable teljesen lefordítja a kódot az IB-ben való megjelenítéshez, ezért nem szabad benne hálózati kéréseket vagy hosszú műveleteket végrehajtani. A megkülönböztetéshez a #if TARGET_INTERFACE_BUILDER (k) használatos — feltételes fordítás, amely kizárja az IB számára nem szánt kódot.
Az Interface Builder fájlok átalakítási folyamata a nib létrehozásától a képernyőn való megjelenésig több szakaszból áll. Ennek a ciklusnak a megértése segít az IB-vel kapcsolatos problémák diagnosztizálásában.
A build fázisban az Xcode elindítja az ibtool (k, fn) eszközt — egy parancssori segédprogramot a .storyboard és .xib fájlok bináris nib formátumba fordításához. Az ibtool validálást is végez: ellenőrzi a korlátozások helyességét, az összes osztály meglétét, az IBOutlet/IBAction kapcsolatok típusait. A validálási hibák az Xcode Issue Navigatorában jelennek meg.
A végső .nib archívum az alkalmazás bundle-jébe kerül a .nib (s) mappába. A nib fájl mérete jelentősen kisebb, mint az eredeti XML: a bináris formátum optimalizált reprezentációt használ, a karakterláncokat tokenekkel helyettesíti és a numerikus értékeket tömöríti. A tipikus tömörítés az eredeti XML méretének 50–60%-a.
Futásidőben a nib a UINib(nibName:bundle:) (k) vagy automatikusan a UIStoryboard.instantiateViewController(withIdentifier:) (k) segítségével töltődik be. A betöltési folyamat a következőket foglalja magában:
awakeFromNib() (fn) meghívása minden objektumhoz — belépési pont a betöltés utáni konfigurációhozAz awakeFromNib() (fn) metódus az összes IBOutlet beállítása után, de az első layoutSubviews előtt hívódik meg. Ez kényelmes a kezdeti konfigurációhoz: lekerekítések beállítása, árnyékok hozzáadása, szöveg lokalizációja. Az összes IBOutlet már garantáltan nem nil az awakeFromNib-ben.
A SwiftUI 2019-es megjelenésével az iOS fejlesztők alternatívát kaptak az Interface Builder helyett — egy deklaratív keretrendszert Canvas Preview valós idejű előnézettel. Elemezzük a két megközelítés legfontosabb különbségeit.
Interface Builder XML leírást generál, amely nib-be fordul. A felület vizuálisan jön létre, a kód csak a logikáért felel. Az IB alacsonyabb belépési küszöböt igényel a programozási készségekkel nem rendelkező tervezők számára, de bonyolult a kód áttekintésében (az XML változások nem láthatók a diff-ben).
SwiftUI Preview — teljesen kód alapú fejlesztés. A felület Swift nyelven íródik le, az előnézet minden mentéskor frissül. Nincs XML, nincs nib, nincs IBOutlet kapcsolatok megszakadásának kockázata. A SwiftUI Preview gyorsabban működik, mint az IB, mert nem igényel külön fájl fordítást.
| Szempont | Interface Builder (UIKit) | SwiftUI Preview |
|---|---|---|
| Fájlformátum | XML (.storyboard / .xib) → bináris nib | Swift kód (nincs köztes fájl) |
| Előnézet | IB vászon késleltetéssel összetett view-knál | Canvas Preview valós időben |
| iOS verziótámogatás | iOS 2.0+ (minden verzió) | iOS 13+ |
| Merge git-ben | Problémás (egy XML fájl) | Egyszerű (normál Swift kód) |
| Dinamikus adatok | IBOutlet + kód segítségével | @State (k), @Observable (k) |
| Egyedi view-k | @IBDesignable (fordítás) | SwiftUI View PreviewProvider-rel |
| Teljesítmény | Gyors nib betöltés | Azonnali Swift fordítás |
A gyakorlatban az IB és a SwiftUI Preview közötti választás a projekt követelményeitől függ. Interface Builder nélkülözhetetlen a régebbi iOS verziókat támogató UIKit alkalmazásokhoz, valamint olyan kereskedelmi projektekhez, ahol a tervezők Xcode-ban dolgoznak Swift készségek nélkül. A SwiftUI az iOS 17+ célú új projektekhez ajánlott, ahol a fejlesztési sebesség és a reaktivitás fontos.
Az Apple nem tervezi az Interface Builder eltávolítását az Xcode-ból. Sőt, az Xcode 16-ban a vállalat javította az IB vászon teljesítményét és hozzáadta a SwiftUI komponensek támogatását a UIViewRepresentable Bridge-en keresztül. Várhatóan az IB legalább 2030-ig támogatva lesz.
A többéves iOS fejlesztési tapasztalat olyan ajánlásokat alakított ki, amelyek csökkentik az Interface Builder kereskedelmi projektekben való használata során felmerülő problémák számát.
Használjon XIB-t a Storyboard helyett az újrafelhasználható komponensekhez. Minden egyedi táblázatcella, fejléc vagy lábléc külön XIB-ben legyen. Ez megkönnyíti a merge-t, gyorsítja a betöltést és lehetővé teszi a komponensek újrafelhasználását projektek között a Swift Package Manager vagy a CocoaPods segítségével.
Állítson be Storyboard References a nagy storyboardok modulokra bontásához. Egyetlen Main.storyboard helyett 100 képernyővel hozzon létre egy storyboard-ot minden modulhoz (Auth, Profile, Feed), és kapcsolja össze őket Storyboard Reference segítségével. Ez csökkenti az ibtool fordítási idejét és egyszerűsíti a csapatmunkát.
Kerülje az IBOutlet kapcsolatokat a File's Owner-höz (k) ellenőrzés nélkül. Minden kapcsolatnak weak (k) és opcionálisnak kell lennie (az implicitly unwrapped optional csak playgroundban jó). Amikor átnevez egy IBOutlet-et a view-ban, az Xcode automatikusan frissíti a kapcsolatot, de az XML kézi szerkesztésekor könnyű hibát elkövetni.
Show Connection Panel-t (k) — a piros jelzők megszakadt kapcsolatokat jeleznekUser Defined Runtime Attributes-ot (k) tulajdonságok beállításához kód nélkül: layer.cornerRadius, layer.borderWidth, tintColorIdentifier-t (k) minden korlátozáshoz a Size Inspector-ban — segít a hibakeresésben konfliktusok eseténimport UIKit
final class ProfileHeaderView: UIView {
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var bioLabel: UILabel!
@IBOutlet weak var editButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
avatarImageView.layer.cornerRadius = avatarImageView.bounds.width / 2
avatarImageView.layer.masksToBounds = true
nameLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bioLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
}
func configure(with profile: UserProfile) {
nameLabel.text = profile.fullName
bioLabel.text = profile.bio
/// Avatar betöltése SDWebImage vagy Kingfisher segítségével
}
static func instantiateFromNib() -> ProfileHeaderView {
let nib = UINib(nibName: String(describing: self), bundle: nil)
return nib.instantiate(withOwner: nil).first as! ProfileHeaderView
}
}A példában egy XIB view-khoz tartozó legjobb gyakorlat látható: a instantiateFromNib (fn) statikus metódus betölti a view-t az osztállyal megegyező nevű XIB-ből. Az awakeFromNib (fn) metódus konfigurálja az UI-t (lekerekítések, betűtípusok), a configure(with:) (fn) metódus pedig egy adatmodellt fogad a kitöltéshez. A felelősségek szétválasztása egyszerűsíti a tesztelést és az újrafelhasználást.
Gyakran ismételt kérdések
Interface Builder — vizuális szerkesztő UIKithez Storyboard/XIB formátummal, drag-and-drop segítségével működik. SwiftUI Preview — valós idejű deklaratív előnézet, ahol a felület Swift kóddal írható le. Mindkét eszköz beépült az Xcode-ba, de az IB XML-t generál, míg a SwiftUI közvetlenül Swift-et fordít. Az IB iOS 2.0+-t, a SwiftUI iOS 13+-t támogatja.
Nem, az Interface Builder nem kompatibilis közvetlenül a SwiftUI-val. A SwiftUI saját deklaratív szintaxisát és Canvas Preview-ját használja. Az IB-n keresztül létrehozott UIKit projektek azonban integrálhatók a SwiftUI-ba UIViewRepresentable segítségével, és a SwiftUI view-k beágyazhatók a UIKit-be UIHostingController segítségével. Ez lehetővé teszi a fokozatos migrációt IB-ről SwiftUI-ra.
@IBDesignable — egy Swift annotáció, amely egy egyedi UIView-t jelenít meg közvetlenül az Interface Builder-ben valós időben, az alkalmazás elindítása nélkül. @IBInspectable — egy annotáció tulajdonságokhoz, amely hozzáadja azokat az IB Attributes Inspector paneljéhez. Mindkét annotáció felgyorsítja az egyedi UI komponensek fejlesztését: csak módosítson egy tulajdonságot az inspektorban — a változás azonnal látható a vásznon.
Az Auto Layout az Interface Builder-ben a Pin (margók, szélesség, magasság) és az Align (középre igazítás, baseline) menük segítségével állít be korlátozásokat. Minden korlátozás egy matematikai kapcsolat a view-k között. Az IB a hibákat piros vonalakkal, a konfliktusokat sárga figyelmeztetésekkel jelzi. A Size Classes az IB-ben lehetővé teszi különböző korlátozások beállítását különböző eszközökhöz és tájolásokhoz kód írása nélkül.
IBOutlet — egy annotáció egy UI elem kódból való hivatkozásához (például @IBOutlet weak var label: UILabel!). IBAction — egy annotáció egy eseménykor meghívott metódushoz (például @IBAction func buttonTapped(_ sender: UIButton)). A kapcsolat Ctrl+drag segítségével jön létre az IB vászonról a vezérlő fájlba. Az Xcode automatikusan létrehozza a kapcsolat kódját az egér elengedésekor.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is