mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-28 06:25:04 -04:00
Library collection view basic function
This commit is contained in:
parent
ddfde941ea
commit
1a72bd0d14
Binary file not shown.
Before Width: | Height: | Size: 479 B |
@ -10,7 +10,7 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "more-2.png",
|
||||
"filename" : "rsz_1more-2.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
BIN
Kiwix-iOS/Assets.xcassets/Dots.imageset/rsz_1more-2.png
vendored
Normal file
BIN
Kiwix-iOS/Assets.xcassets/Dots.imageset/rsz_1more-2.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -39,7 +39,7 @@ class BookmarkCollectionController: CoreDataCollectionBaseController, UICollecti
|
||||
|
||||
func configureItemWidth(collectionViewWidth: CGFloat) {
|
||||
let itemsPerRow = ((collectionViewWidth - 10) / 320).rounded()
|
||||
self.itemWidth = floor((collectionViewWidth - (itemsPerRow + 1) * 10) / itemsPerRow)
|
||||
itemWidth = floor((collectionViewWidth - (itemsPerRow + 1) * 10) / itemsPerRow)
|
||||
}
|
||||
|
||||
// MARK: - UI Control
|
||||
|
@ -8,14 +8,159 @@
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
import ProcedureKit
|
||||
import DZNEmptyDataSet
|
||||
|
||||
class LibraryBooksController: UIViewController {
|
||||
|
||||
@IBOutlet weak var collectionView: UICollectionView!
|
||||
class LibraryBooksController: CoreDataCollectionBaseController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, LibraryCollectionCellDelegate {
|
||||
private(set) var itemWidth: CGFloat = 0.0
|
||||
|
||||
@IBAction func dismissButtonTapped(_ sender: UIBarButtonItem) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func configureItemWidth(collectionViewWidth: CGFloat) {
|
||||
let itemsPerRow = (collectionViewWidth / 320).rounded()
|
||||
itemWidth = (collectionViewWidth - 1 * (itemsPerRow - 1)) / itemsPerRow
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
configureRefreshControl()
|
||||
|
||||
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
|
||||
layout.minimumInteritemSpacing = 1
|
||||
layout.minimumLineSpacing = 1
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
refreshAutomatically()
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
configureItemWidth(collectionViewWidth: collectionView.frame.width)
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
configureItemWidth(collectionViewWidth: collectionView.frame.width)
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
}
|
||||
|
||||
// MARK: - Refresh
|
||||
|
||||
private(set) var isRefreshing = false // used to control text on empty table view
|
||||
|
||||
private func configureRefreshControl() {
|
||||
collectionView.refreshControl = RefreshLibControl()
|
||||
collectionView.refreshControl?.addTarget(self, action: #selector(refresh), for: .valueChanged)
|
||||
}
|
||||
|
||||
func refreshAutomatically() {
|
||||
guard let date = Preference.libraryLastRefreshTime else { refresh(); return }
|
||||
guard date.timeIntervalSinceNow < -86400 else {return}
|
||||
refresh()
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
let operation = RefreshLibraryOperation()
|
||||
operation.add(observer: WillExecuteObserver { (operation) in
|
||||
OperationQueue.main.addOperation({
|
||||
// Configure empty table data set, so it shows "Refreshing..."
|
||||
self.isRefreshing = true
|
||||
self.collectionView.reloadEmptyDataSet()
|
||||
})
|
||||
})
|
||||
operation.add(observer: DidFinishObserver { (operation, errors) in
|
||||
guard let operation = operation as? RefreshLibraryOperation else {return}
|
||||
OperationQueue.main.addOperation({
|
||||
defer {
|
||||
self.collectionView.refreshControl?.endRefreshing()
|
||||
self.isRefreshing = false
|
||||
self.collectionView.reloadEmptyDataSet()
|
||||
}
|
||||
|
||||
if let _ = errors.first {
|
||||
// handle error [network, xmlparse]
|
||||
} else {
|
||||
if operation.firstTime {
|
||||
//self.showLanguageFilterAlert()
|
||||
//self.configureNavBarButtons()
|
||||
} else {
|
||||
// self.showRefreshSuccessMessage()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
GlobalQueue.shared.add(operation: operation)
|
||||
}
|
||||
|
||||
// MARK: - UICollectionView Data Source
|
||||
|
||||
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
return fetchedResultController.sections?.count ?? 0
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return fetchedResultController.sections?[section].numberOfObjects ?? 0
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! LibraryCollectionCell
|
||||
|
||||
let book = fetchedResultController.object(at: indexPath)
|
||||
cell.delegate = self
|
||||
cell.imageView.image = UIImage(data: book.favIcon ?? Data())
|
||||
cell.titleLabel.text = book.title
|
||||
cell.subtitleLabel.text = [
|
||||
book.dateDescription,
|
||||
book.fileSizeDescription,
|
||||
book.articleCountDescription
|
||||
].flatMap({$0}).joined(separator: " ")
|
||||
cell.descriptionLabel.text = book.desc
|
||||
cell.hasPicLabel.isHidden = book.hasPic
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "Header", for: indexPath) as! LibraryCollectionHeader
|
||||
header.textLabel.text = fetchedResultController.sections?[indexPath.section].name
|
||||
return header
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegateFlowLayout
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
return CGSize(width: itemWidth, height: 66)
|
||||
}
|
||||
|
||||
// MARK: - LibraryCollectionCellDelegate
|
||||
|
||||
func didTapMoreButton(cell: LibraryCollectionCell) {
|
||||
guard let indexPath = collectionView.indexPath(for: cell) else {return}
|
||||
print("\(indexPath) tapped")
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NSFetchedResultsController
|
||||
|
||||
let managedObjectContext = AppDelegate.persistentContainer.viewContext
|
||||
lazy var fetchedResultController: NSFetchedResultsController<Book> = {
|
||||
let fetchRequest = Book.fetchRequest()
|
||||
let langDescriptor = NSSortDescriptor(key: "language.name", ascending: true)
|
||||
let titleDescriptor = NSSortDescriptor(key: "title", ascending: true)
|
||||
fetchRequest.sortDescriptors = [langDescriptor, titleDescriptor]
|
||||
fetchRequest.predicate = NSPredicate(format: "language.name != nil")
|
||||
//// fetchRequest.predicate = self.onlineCompoundPredicate
|
||||
|
||||
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
|
||||
managedObjectContext: self.managedObjectContext,
|
||||
sectionNameKeyPath: "language.name", cacheName: nil)
|
||||
controller.delegate = self
|
||||
try? controller.performFetch()
|
||||
return controller as! NSFetchedResultsController<Book>
|
||||
}()
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -44,22 +45,118 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="FMe-0w-Xez">
|
||||
<size key="itemSize" width="50" height="50"/>
|
||||
<size key="itemSize" width="320" height="66"/>
|
||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||
<size key="footerReferenceSize" width="50" height="22"/>
|
||||
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="Aea-Om-Uku">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="Aea-Om-Uku" customClass="LibraryCollectionCell" customModule="Kiwix" customModuleProvider="target">
|
||||
<rect key="frame" x="28" y="0.0" width="320" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Wbk-Lp-yTb">
|
||||
<rect key="frame" x="8" y="8" width="50" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="50" id="Lfz-Bj-qPh"/>
|
||||
<constraint firstAttribute="height" constant="50" id="Xc4-8W-tr6"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="q2k-12-zti">
|
||||
<rect key="frame" x="66" y="8" width="42" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rmy-yO-cBy">
|
||||
<rect key="frame" x="66" y="29" width="31" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wmd-Qv-2ia">
|
||||
<rect key="frame" x="66" y="43.5" width="31" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Pic" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aKZ-e6-Ikq">
|
||||
<rect key="frame" x="117" y="9.5" width="26" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="58f-me-IXF"/>
|
||||
<constraint firstAttribute="width" constant="26" id="FBn-H0-tNj"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="12"/>
|
||||
<color key="textColor" red="1" green="0.50196081400000003" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nTw-d7-YqD">
|
||||
<rect key="frame" x="270" y="10" width="42" height="46"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="42" id="lBA-pd-Pa2"/>
|
||||
<constraint firstAttribute="height" constant="46" id="qqN-B2-nid"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Dots"/>
|
||||
<connections>
|
||||
<action selector="moreButtonTapped:" destination="Aea-Om-Uku" eventType="touchUpInside" id="Cxh-DD-Tyi"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<color key="backgroundColor" red="1" green="0.50196081400000003" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Wbk-Lp-yTb" firstAttribute="centerY" secondItem="Aea-Om-Uku" secondAttribute="centerY" id="3Lj-sz-46B"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="nTw-d7-YqD" secondAttribute="trailing" id="5JR-74-PhA"/>
|
||||
<constraint firstItem="wmd-Qv-2ia" firstAttribute="leading" secondItem="Wbk-Lp-yTb" secondAttribute="trailing" constant="8" id="6DI-9y-Igu"/>
|
||||
<constraint firstItem="aKZ-e6-Ikq" firstAttribute="leading" secondItem="q2k-12-zti" secondAttribute="trailing" constant="9" id="Bvy-Bq-O5f"/>
|
||||
<constraint firstItem="aKZ-e6-Ikq" firstAttribute="centerY" secondItem="q2k-12-zti" secondAttribute="centerY" constant="-1" id="LIs-SX-crF"/>
|
||||
<constraint firstItem="wmd-Qv-2ia" firstAttribute="top" secondItem="rmy-yO-cBy" secondAttribute="bottom" id="MEV-c5-uqZ"/>
|
||||
<constraint firstItem="rmy-yO-cBy" firstAttribute="leading" secondItem="Wbk-Lp-yTb" secondAttribute="trailing" constant="8" id="Mn8-60-6ii"/>
|
||||
<constraint firstItem="nTw-d7-YqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="rmy-yO-cBy" secondAttribute="trailing" id="TOj-hZ-bSG"/>
|
||||
<constraint firstItem="nTw-d7-YqD" firstAttribute="centerY" secondItem="Aea-Om-Uku" secondAttribute="centerY" id="VLl-VW-z7E"/>
|
||||
<constraint firstItem="q2k-12-zti" firstAttribute="leading" secondItem="Wbk-Lp-yTb" secondAttribute="trailing" constant="8" id="g7u-6E-fy5"/>
|
||||
<constraint firstAttribute="leadingMargin" secondItem="Wbk-Lp-yTb" secondAttribute="leading" id="hkU-Yw-gmi"/>
|
||||
<constraint firstItem="nTw-d7-YqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="aKZ-e6-Ikq" secondAttribute="trailing" id="pmb-V3-HIM"/>
|
||||
<constraint firstAttribute="topMargin" secondItem="q2k-12-zti" secondAttribute="top" id="qG1-In-0ae"/>
|
||||
<constraint firstItem="nTw-d7-YqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="wmd-Qv-2ia" secondAttribute="trailing" id="qRe-Xm-jjX"/>
|
||||
<constraint firstItem="rmy-yO-cBy" firstAttribute="top" secondItem="q2k-12-zti" secondAttribute="bottom" id="tUM-uu-aX5"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="descriptionLabel" destination="wmd-Qv-2ia" id="SYF-xK-ewN"/>
|
||||
<outlet property="hasPicLabel" destination="aKZ-e6-Ikq" id="ZgS-qb-zdm"/>
|
||||
<outlet property="imageView" destination="Wbk-Lp-yTb" id="QZJ-hP-zJr"/>
|
||||
<outlet property="subtitleLabel" destination="rmy-yO-cBy" id="sx5-Ij-QaQ"/>
|
||||
<outlet property="titleLabel" destination="q2k-12-zti" id="LU3-St-SwQ"/>
|
||||
</connections>
|
||||
</collectionViewCell>
|
||||
</cells>
|
||||
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Header" id="Vew-wk-bdN" customClass="LibraryCollectionHeader" customModule="Kiwix" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="66" width="375" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uBe-10-mcY">
|
||||
<rect key="frame" x="28" y="3" width="36" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="uBe-10-mcY" firstAttribute="centerY" secondItem="Vew-wk-bdN" secondAttribute="centerY" id="bOA-89-aKD"/>
|
||||
<constraint firstItem="uBe-10-mcY" firstAttribute="leading" secondItem="Vew-wk-bdN" secondAttribute="leadingMargin" constant="20" id="iWL-kS-Hu6"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textLabel" destination="uBe-10-mcY" id="pab-il-9r3"/>
|
||||
</connections>
|
||||
</collectionReusableView>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="cDq-va-k4I" id="C1Z-rn-cDB"/>
|
||||
<outlet property="delegate" destination="cDq-va-k4I" id="O4e-0Y-jYL"/>
|
||||
</connections>
|
||||
</collectionView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
@ -144,5 +241,6 @@
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Cross" width="16" height="16"/>
|
||||
<image name="Dots" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -101,8 +101,6 @@ class ArticleSnippetCell: ArticleCell {
|
||||
@IBOutlet weak var snippetLabel: UILabel!
|
||||
}
|
||||
|
||||
// MARK: - Bookmark Cell
|
||||
|
||||
class BookmarkCollectionCell: UICollectionViewCell {
|
||||
override func awakeFromNib() {
|
||||
clipsToBounds = false
|
||||
@ -144,6 +142,35 @@ class BookmarkCollectionCell: UICollectionViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryCollectionCell: UICollectionViewCell {
|
||||
override func awakeFromNib() {
|
||||
imageView.clipsToBounds = true
|
||||
imageView.layer.cornerRadius = 2.0
|
||||
hasPicLabel.layer.borderWidth = 1.5
|
||||
hasPicLabel.layer.borderColor = UIColor.orange.cgColor
|
||||
hasPicLabel.layer.cornerRadius = 8.0
|
||||
}
|
||||
|
||||
@IBAction func moreButtonTapped(_ sender: UIButton) {
|
||||
delegate?.didTapMoreButton(cell: self)
|
||||
}
|
||||
|
||||
weak var delegate: LibraryCollectionCellDelegate?
|
||||
@IBOutlet weak var imageView: UIImageView!
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
@IBOutlet weak var subtitleLabel: UILabel!
|
||||
@IBOutlet weak var descriptionLabel: UILabel!
|
||||
@IBOutlet weak var hasPicLabel: UILabel!
|
||||
}
|
||||
|
||||
protocol LibraryCollectionCellDelegate: class {
|
||||
func didTapMoreButton(cell: LibraryCollectionCell)
|
||||
}
|
||||
|
||||
class LibraryCollectionHeader: UICollectionReusableView {
|
||||
@IBOutlet weak var textLabel: UILabel!
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user