I want to create sticky header for particular section in UICollectionView in Swift - uicollectionview

I used collection view with 3 different sections where 1st section shows banner, 2nd section shows the sub categories and the 3rd shows the items/products, now what I want is when I scroll the whole view the 2nd section should work as a sticky header on scrolling. 
I have done all the possible things that can be done, but nowhere near to solve the issue
Kindly help me out
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0: return 1
case 1: return subCategories.count
case 2: return selectedSubCategoryID == -1 ? products.count : productsSub.count
default: return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.section {
case 0:
let cell = collectionView.dequeueReusableCell(ofType: ShopBannerCell.self, for: indexPath)
cell.shopLogoImageView.loadImage(shop.logo)
cell.shopLogoImageView.layer.borderColor = UIColor.lightGray.cgColor
cell.shopLogoImageView.layer.borderWidth = 1
cell.shopBannerImageView.loadImage(details?.sliders?.first)
cell.images = details?.sliders ?? []
cell.shopNameLabel.text = shop.name
// cell.lcoationButton.setTitle(details?.address, for: .normal)
cell.showSideMenu = { [weak self] sender in
if let navVC = self?.navigationController as? NavigationController {
navVC.showSideMenu()
}
}
if currentLanguage == "en" {
cell.backArrowBtn.setImage(#imageLiteral(resourceName: "left_arrow"), for: .normal)
} else
{
cell.backArrowBtn.setImage(#imageLiteral(resourceName: "right-arrow"), for: .normal)
}
cell.backButton = { [weak self] sender in
self?.navigationController?.popViewController(animated: true)
}
cell.showSearch = { [weak self] sender in
NotificationCenter.default.post(name: .navigateToShopVC, object: self)
}
cell.showShopVC = { [weak self] sender in
self?.navigationController?.popToRootViewController(animated: true)
}
cell.showFilterVC = { [weak self] sender in
let productFilterVC = ProductFilterVC()
productFilterVC.modalPresentationStyle = .overFullScreen
productFilterVC.showShopVC = { [weak self] in
self?.navigationController?
.popToRootViewController(animated: true)
}
// variant_product
productFilterVC.applyFilters = { [weak self] filter in
if let priceRange = filter.priceRange, !priceRange.isEmpty, priceRange != 0...0 {
if self?.selectedSubCategoryID == -1 {
self?.products = self?.products.filter { priceRange.contains(Int($0.basePrice ?? 0))} ?? []
} else {
self?.productsSub = self?.productsSub.filter { priceRange.contains(Int($0.unitPrice ?? "0") ?? 0)} ?? []
}
}
if filter.sortOrder != -1 {
if self?.selectedSubCategoryID == -1 {
self?.products.sort(by: { item1, item2 -> Bool in
switch filter.sortOrder {
case 0: return item1.basePrice ?? 0 < item2.basePrice ?? 0
case 1: return item1.basePrice ?? 0 > item2.basePrice ?? 0
case 2: return item1.discount ?? 0 < item2.discount ?? 0
default: return true
}
})
} else {
self?.productsSub.sort(by: { item1, item2 -> Bool in
switch filter.sortOrder {
case 0: return Double(item1.unitPrice ?? "") ?? 0 < Double(item2.unitPrice ?? "") ?? 0
case 1: return Double(item1.unitPrice ?? "") ?? 0 > Double(item2.unitPrice ?? "") ?? 0
case 2: return Double(item1.discount ?? "") ?? 0 < Double(item2.discount ?? "") ?? 0
default: return true
}
})
}
}
self?.collectionView.reloadSections(IndexSet(integer: 2))
}
self?.present(productFilterVC, animated: true)
}
cell.gridButton.isSelected = self.selectedLayout == .grid
cell.listButton.isSelected = self.selectedLayout == .list
cell.listlayout = { [weak self] sender in
self?.selectedLayout = .list
self?.collectionView.collectionViewLayout.invalidateLayout()
self?.collectionView.reloadData()
}
cell.gridLayout = { [weak self] sender in
self?.selectedLayout = .grid
self?.collectionView.collectionViewLayout.invalidateLayout()
self?.collectionView.reloadData()
}
return cell
case 1:
let cell = collectionView.dequeueReusableCell(ofType: SubCategoryCell.self, for: indexPath)
let subCategory = subCategories[indexPath.row]
if subCategory.id == selectedSubCategoryID {
cell.button.isSelected = true
cell.button.backgroundColor = .accentColor
} else {
cell.button.isSelected = false
cell.button.backgroundColor = .clear
}
if currentLanguage == "en" {
cell.button.setTitle(subCategory.name?.capitalized, for: .normal)
} else
{
cell.button.setTitle(subCategory.ar_name?.capitalized, for: .normal)
}
cell.onPress = { [weak self] sender in
guard let self = self else { return }
self.selectedSubCategoryID = self.subCategories[indexPath.row].id ?? 1
}
return cell
case 2:
if selectedLayout == .grid {
let cell = collectionView.dequeueReusableCell(ofType: ProductCell.self, for: indexPath)
// cell.addToCartButton.setTitle("Add to Cart".localized(), for: .normal)
let image = selectedSubCategoryID == -1 ? products[indexPath.row].photos.first : productsSub[indexPath.row].photos?.first
cell.productImageView.loadImage(image as? String)
if currentLanguage == "en"{
cell.productNameLabel.text = selectedSubCategoryID == -1 ? products[indexPath.row].name : productsSub[indexPath.row].name
} else {
cell.productNameLabel.text = selectedSubCategoryID == -1 ? products[indexPath.row].arName : productsSub[indexPath.row].arName
}
cell.pricingLabel.text = "KD ".localized() + "\(products[indexPath.row].baseDiscountedPrice ?? "")"
if products[indexPath.row].basePrice == Double(products[indexPath.row].baseDiscountedPrice ?? ""){
cell.discountView.isHidden = true
cell.pricingLabel.textAlignment = .center
cell.pricingLabel.textColor = UIColor.black
}
else
{
cell.discountView.isHidden = false
cell.discountedLBL.text = "KD " + (String(products[indexPath.row].basePrice ?? 0)) + "0"
cell.pricingLabel.textColor = UIColor.red
}
// let price = selectedSubCategoryID == -1 ? "\(products[indexPath.row].basePrice ?? 0)" : productsSub[indexPath.row].unitPrice ?? ""
return cell
} else {
let cell = collectionView.dequeueReusableCell(ofType: ProductListCell.self, for: indexPath)
// cell.addToCartButton.setTitle("Add to Cart".localized(), for: .normal)
let image = selectedSubCategoryID == -1 ? products[indexPath.row].photos.first : productsSub[indexPath.row].photos?.first
cell.productImageView.loadImage(image as? String)
cell.productImageView.layer.borderColor = UIColor.lightGray.cgColor
cell.productImageView.layer.borderWidth = 1
cell.pricingLabel.text = "KD ".localized() + "\(products[indexPath.row].baseDiscountedPrice ?? "")"
if products[indexPath.row].basePrice == Double(products[indexPath.row].baseDiscountedPrice ?? ""){
cell.discountView.isHidden = true
cell.pricingLabel.textAlignment = .center
cell.pricingLabel.textColor = UIColor.black
}
else
{
cell.discountView.isHidden = false
cell.discountLbl.text = "KD " + (String(products[indexPath.row].basePrice ?? 0)) + "0"
cell.pricingLabel.textColor = UIColor.red
}
if currentLanguage == "en"{
cell.productNameLabel.text = selectedSubCategoryID == -1 ? products[indexPath.row].name : productsSub[indexPath.row].name
} else {
cell.productNameLabel.text = selectedSubCategoryID == -1 ? products[indexPath.row].arName : productsSub[indexPath.row].arName
}
// let price = selectedSubCategoryID == -1 ? "\(products[indexPath.row].basePrice ?? 0)" : productsSub[indexPath.row].unitPrice ?? ""
let productID = selectedSubCategoryID == -1 ? "\(products[indexPath.row].id ?? 0)" : "\(productsSub[indexPath.row].id ?? "")"
cell.onAddToCart = { [weak self] _ in
if UserRepository.shared.userName == "GUEST" {
self?.promptForGuestUser()
return
}
self?.addToCartAPI(product: "\(productID)")
}
return cell
}
default:
return collectionView.dequeueReusableCell(ofType: SubCategoryCell.self, for: indexPath)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
switch indexPath.section {
case 2:
let product = products[indexPath.row]
let vc = ProductDetailsVC(id: "\(product.id ?? 0)",
subCategoryID: product.subcategoryID ?? "",
sellerID: product.sellerID ?? "")
navigationController?.pushViewController(vc, animated: true)
default: break
}
}

Related

SwiftUI: How to force user to only input one single digit?

I have a bunch of textfield that only accept numbers. But how can I force the user to only input one single digit and not multiple. I want the input to be a number between 0 and 9.
Any ideas?
This is the setup for one of the textfields:
CocoaTextField(pin1default, text: $pin1)
.isFirstResponder(currentFocus == 0)
.font(.system(size: 18, weight: .bold))
.lineLimit(1)
.frame(maxWidth: 50, maxHeight: 50, alignment: .center)
.multilineTextAlignment(.center)
.overlay(
RoundedRectangle(cornerRadius: 6)
.strokeBorder(Color.gray.opacity(0.1), lineWidth: 2, antialiased: true)
)
.keyboardType(.numberPad)
.onChange(of: pin1, perform: { value in
if value.count > 0 {
currentFocus += 1
}
})
.onTapGesture {
currentFocus = 0
pin1default = ""
}
You could do something like this:
func validateTextField(_ number: String) -> Bool {
let numberPredicate = NSPredicate(format: "SELF MATCHES %#", "^[0-9]*$")
return numberPredicate.evaluate(with: number) ? true : false
}
struct ContentView: View {
#State var number = ""
var body: some View {
TextField("Number", text: $number)
.onChange(of: number) { newNumber in
if !validateTextField(newNumber) && newNumber.count <= 1 {
number = ""
} else {
let value = String(newNumber.prefix(1))
if newNumber != value {
number = value
}
}
}
}
}

im having difficulty getting downloaded json response into my data model and accessing it in code

i have the following code in which i'm trying to download exchange rates into my app to use in currency conversion.
The data fetch seems to work ok, as does the json decoding model, but i'm unable to get the data through the Rates variable
import SwiftUI
struct ExchangeRates: Codable {
var conversionRates: [String: Double]?
init(conversionRates:[String:Double]) {
self.conversionRates = conversionRates
}
enum CodingKeys: String, CodingKey {
case conversionRates = "conversion_rates"
}
}
class DownloadingData:ObservableObject{
#Published var Rates:ExchangeRates = ExchangeRates.init(conversionRates: ["test" : 0])
init() {
datatask()
}
func datatask() {
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(String(describing: error))")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let rates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(rates) **// this works and prints out data**
DispatchQueue.main.async {
[weak self] in
self?.Rates = rates
print(self?.Rates) **// this works and prints out the data**
}
print(self.Rates) **// this doesnt print out anything**
}.resume()
print(Rates) **// this doesnt print out anything**
}
}
i can't seem to get the data into the Rates Variable
any guidance please
thanks
here is a sample of the console output :
ExchangeRates(conversionRates: Optional(["test": 0.0]))
Optional(test3.ExchangeRates(conversionRates: Optional(["BZD": 2.7002, "PHP": 68.7948, "PGK": 4.725, "BND": 1.8176, "HNL": 32.8885, "TND": 3.7553, "BDT": 115.2218, "SBD": 10.6866, "NIO": 47.4824, "XDR": 0.963, "IDR": 19213.9064, "XCD": 3.6453, "CAD": 1.7152, "UGX": 4778.6135,])
you could try this, using your ExchangeRates struct:
class DownloadingData: ObservableObject{
#Published var rates = ExchangeRates(conversionRates: ["test" : 0])
init() {
datatask()
}
func datatask() {
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(error)")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let exRates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(exRates)
DispatchQueue.main.async {
self.rates = exRates // <--- here
print(self.rates) // <--- here
}
// print(self.rates) // <--- NEVER here
}.resume()
}
}
EDIT-1: with completion closure:
class DownloadingData: ObservableObject{
#Published var rates = ExchangeRates(conversionRates: ["test" : 0])
init() {
datatask() { isDone in
print(self.rates) // <--- here OK
}
}
func datatask(completion: #escaping(Bool) -> ()) { // <--- here
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(error)")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let exRates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(exRates) // <--- here OK
DispatchQueue.main.async {
self.rates = exRates
print(self.rates) // <--- here OK
completion(true) // <--- here return completion
}
}.resume()
}
}
Declare rates – please with starting lowercase letter –  as empty dictionary
#Published var rates = [String:Double]()
In the struct delete the init method and declare the dictionary also non-optional
struct ExchangeRates: Decodable {
let conversionRates: [String: Double]
enum CodingKeys: String, CodingKey {
case conversionRates = "conversion_rates"
}
}
in the DispatchQueue closure assign the value of conversionRates to rates
DispatchQueue.main.async { // no weak self needed
self.rates = rates.conversionRates
}
In the view enumerate the dictionary rates

Collectionview cellForItemAtIndexPath not call after reload

i have 2 dictionary and dictionary contain array of passing in numberOfItemsInSection
1. self.dicMyClaimsSender
2. self.dicMyClaimsReceiver
I am using segment control. When I tap on segment, then I reload the collectionView (I am using 3 collection view).
// UISegmentedControl method
#IBAction func segmntAction(sender:UISegmentedControl) {
switch segmnetCtrl.selectedSegmentIndex {
case 0:
self.isSenderSelected = true
self.heightCollection.constant = 130.0
self.heightViewImg.constant = 0.0
self.heightViewVideo.constant = 0.0
self.heightViewFile.constant = 0.0
self.heightBtnAttach.constant = 42.0
self.heightBtnSendFile.constant = 42.0
self.collectionImg.reloadData()
self.collectionFile.reloadData()
self.collectionVideo.reloadData()
break
case 1:
self.isSenderSelected = false
self.heightViewImg.constant = 156.0
self.heightViewVideo.constant = 156.0
self.heightViewFile.constant = 156.0
self.heightCollection.constant = 0.0
self.heightBtnAttach.constant = 0.0
self.heightBtnSendFile.constant = 0.0
self.collectionImg.reloadData()
self.collectionFile.reloadData()
self.collectionVideo.reloadData()
break
default:
break;
}
}
// MARK: - UICollectionView Delegates & Datasources
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if self.isSenderSelected {
if collectionView.tag == 1 {
return self.assets?.count ?? 0
} else if collectionView.tag == 2 {
if self.dicMyClaimsSender.ary_Images.count == 0 {
self.heightViewImg.constant = 0.0
} else {
self.heightViewImg.constant = 156.0
}
return self.dicMyClaimsSender.ary_Images.count
} else if collectionView.tag == 3 {
if self.dicMyClaimsSender.ary_Video.count == 0 {
self.heightViewVideo.constant = 0.0
} else {
self.heightViewVideo.constant = 156.0
}
return self.dicMyClaimsSender.ary_Video.count
} else {
if self.dicMyClaimsSender.ary_Document.count == 0 {
self.heightViewFile.constant = 0.0
} else {
self.heightViewFile.constant = 156.0
}
return self.dicMyClaimsSender.ary_Document.count
}
} else {
if collectionView.tag == 2 {
if self.dicMyClaimsReceiver.ary_Images.count == 0 {
self.heightViewImg.constant = 0.0
} else {
self.heightViewImg.constant = 156.0
}
return self.dicMyClaimsReceiver.ary_Images.count
} else if collectionView.tag == 3 {
if self.dicMyClaimsReceiver.ary_Video.count == 0 {
self.heightViewVideo.constant = 0.0
} else {
self.heightViewVideo.constant = 156.0
}
return self.dicMyClaimsReceiver.ary_Video.count
} else {
if self.dicMyClaimsReceiver.ary_Document.count == 0 {
self.heightViewFile.constant = 0.0
} else {
self.heightViewFile.constant = 156.0
}
return self.dicMyClaimsReceiver.ary_Document.count
}
}
}

UITableview reusable cell issue swift

I have tableview in which each cell contains label,image and button.
My problem is when i disabled first cell and scroll table view 5th cell also disable
here is code
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! DetailTableViewCell
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsetsZero
cell.layoutMargins = UIEdgeInsetsZero
print(indexPath.row)
if indexPath.row == videoName.count
{
cell.video_name?.hidden = true
cell.artist_name?.hidden = true
cell.img?.hidden = true
cell.viewer?.hidden = true
cell.btn_add?.hidden = true
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
indicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
indicator.center = cell.contentView.center
if self.pageToken != ""
{
cell.contentView.addSubview(indicator)
indicator.startAnimating()
if videoIDArr.count == 25 || videoIDArr.count == 5
{
if check == 0
{
loadMoreMostPopularVideo()
}
else
{
loadMorePlaylistVideo()
}
}
}
}
else
{
indicator.stopAnimating()
cell.video_name?.hidden = false
cell.artist_name?.hidden = false
cell.img?.hidden = false
cell.viewer?.hidden = false
cell.btn_add?.hidden = false
tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
cell.video_name?.text = videoName[indexPath.row]
cell.artist_name?.text = artistName[indexPath.row]
cell.img?.sd_setImageWithURL(NSURL(string: self.thumbURL[indexPath.row]))
cell.viewer?.text = "\(viewerArr[indexPath.row]) views"
cell.btn_add.tag = indexPath.row
cell.btn_add?.addTarget(self, action: "addVideo:", forControlEvents: UIControlEvents.TouchUpInside)
// Disable cell code
if dataVideoName.contains(cell.video_name.text!)
{
cell.img.alpha = 0.50
cell.btn_add.alpha = 0.50
cell.viewer.alpha = 0.50
cell.video_name.alpha = 0.50
cell.artist_name.alpha = 0.50
cell.userInteractionEnabled = false
}
}
return cell
}
I have face this problem in morning
give me some suggestion to solve this problem
Thanks
You need enable cell codes for if indexPath.row == videoName.count {} to have cell's settings symmetrically
if indexPath.row == videoName.count {
....
// Enable cell code
if dataVideoName.contains(cell.video_name.text!)
{
cell.img.alpha = 1
cell.btn_add.alpha = 1
cell.viewer.alpha = 1
cell.video_name.alpha = 1
cell.artist_name.alpha = 1
cell.userInteractionEnabled = true
}
} else {
....
// Disable cell code
if dataVideoName.contains(cell.video_name.text!)
{
cell.img.alpha = 0.50
cell.btn_add.alpha = 0.50
cell.viewer.alpha = 0.50
cell.video_name.alpha = 0.50
cell.artist_name.alpha = 0.50
cell.userInteractionEnabled = false
}
}

Change pickerView component position

I have a pickerView with two components . Based on my language ,I need to change the components position so my first component to be at the right and my second component to be at the left .
this is what I wan to do :
Note: The components are connected I mean by selecting a row from first component , the second component would be reloaded with a new content.
How should I do that?
Update
This is the code:
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if IS_Mahalat {
return 1
}else{ // just ostan - shahrestan
if component == 0 { //osatn
return self.ostans.count
}else{//shahrestan
return self.ostans[pickerView.selectedRowInComponent(0)]._city.count
}
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if IS_Mahalat {
//return self.mahalat[row].name
return "sdf"
}else{//ostan shahrestan
if component == 0 { //osatn
return self.ostans[row].name
}else{//shahrestan
print(pickerView.selectedRowInComponent(0))
return self.ostans[pickerView.selectedRowInComponent(0)]._city[row]._name
}
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if !IS_Mahalat {//ostan shahrestan
if component == 0 {
self.picker_ostan.reloadComponent(1)
}
}
}
func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
var myString = view as! UILabel!
let rowText:String!
if( view == nil) {
myString = UILabel()
}
if IS_Mahalat {
rowText = ""
}else{ //ostan shahrestan
if component == 0 { //osatn
rowText = self.ostans[row].name
}else{//shahrestan
rowText = self.ostans[pickerView.selectedRowInComponent(0)]._city[row]._name
}
}
myString.textAlignment = .Center
let attributedRowText = NSMutableAttributedString(string: rowText)
let attributedRowTextLength = attributedRowText.length
attributedRowText.addAttribute(NSForegroundColorAttributeName, value: UIColor.blueColor(), range: NSRange(location: 0, length: attributedRowTextLength))
attributedRowText.addAttribute(NSFontAttributeName, value: UIFont(name: FONT_NAME, size: 16.0)!, range: NSRange(location: 0 ,length:attributedRowTextLength))
myString!.attributedText = attributedRowText
return myString
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
if IS_Mahalat {
return 1
}else{ // just ostan - shahrestan
return 2
}
}