I have a collection view cell which loads images. On each cell I allow users to long press which pops up buttons with icons which can allow users to click on. Upon clicking on the buttons I show the type of icon they clicked on as a form of rating. My issue now is currently if I scroll down and comes up to the top the button on the image which I displayed after the user has clicked is no longer there. How do I keep the icons stacked to the image even if I scroll
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.btnSmallGoodRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallOkRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallHateRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallLoveRate.id = self.movieObj[indexPath.row].id ?? ""
cell.tapHandler = {
Commons.userGlobalRate += 1
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
cell.configure(with: movieObj[indexPath.row])
let ids = ["id" : movieObj[indexPath.row].id!]
excludedDictionary.append(ids)
return cell
}
class MoviesCollectionCell: UICollectionViewCell {
var tapHandler: (()->())?
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
return image
}()
let movieOverlay: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black.withAlphaComponent(0.3)
view.alpha = 0
return view
}()
let btnRate: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFit
image.alpha = 0
return image
}()
let btnSmallGoodRate: ButtonRating = {
let btn = ButtonRating()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setImage(UIImage(named: "goodRate"), for: .normal)
btn.addTarget(self, action: #selector(goodItem(_:)), for: .touchUpInside)
btn.backgroundColor = .black.withAlphaComponent(0.4)
btn.layer.cornerRadius = 30
btn.alpha = 0
return btn
}()
let removeOverlay: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setImage(UIImage(systemName: "xmark"), for: .normal)
btn.tintColor = .black
btn.contentMode = .scaleAspectFit
btn.backgroundColor = .white.withAlphaComponent(0.7)
btn.layer.cornerRadius = 10
btn.contentEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
btn.addTarget(self, action: #selector(dismissOverlay), for: .touchUpInside)
btn.alpha = 0
return btn
}()
let btnSmallHateRate: ButtonRating = {
let btn = ButtonRating()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.clipsToBounds = true
btn.contentMode = .scaleAspectFit
btn.setImage(UIImage(named: "HateIt"), for: .normal)
btn.alpha = 0
btn.addTarget(self, action: #selector(hateItem(_:)), for: .touchUpInside)
btn.backgroundColor = .black.withAlphaComponent(0.4)
btn.layer.cornerRadius = 30
return btn
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(movieImage)
movieImage.addSubview(btnRate)
movieImage.addSubview(movieOverlay)
contentView.addSubview(btnSmallGoodRate)
contentView.addSubview(btnSmallHateRate)
contentView.addSubview(removeOverlay)
let directFullPreviewer = UILongPressGestureRecognizer(target: self, action: #selector(directFullPreviewLongPressAction))
addGestureRecognizer(directFullPreviewer)
btnRate.tintColor = .white
btnRate.layer.shadowColor = UIColor.black.cgColor
btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
btnRate.layer.shadowRadius = 2
btnRate.layer.shadowOpacity = 0.8
btnRate.layer.masksToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
movieImage.image = nil
btnRate.image = nil
}
func configure(with res: Responder){
movieImage.sd_setImage(with: URL(string: res.packShot?.thumbnail ?? ""), placeholderImage: UIImage(named: "ImagePlaceholder"))
}
#objc func directFullPreviewLongPressAction(g: UILongPressGestureRecognizer)
{
if g.state == UIGestureRecognizer.State.began
{
movieOverlay.alpha = 1
btnSmallGoodRate.alpha = 1
btnSmallHateRate.alpha = 1
removeOverlay.alpha = 1
btnHidden.alpha = 1
}
}
#objc func dismissOverlay(){
movieOverlay.alpha = 0
btnSmallHateRate.alpha = 0
btnSmallGoodRate.alpha = 0
removeOverlay.alpha = 0
btnHidden.alpha = 0
}
#objc func hateItem(_ sender: UIButton){
guard let gestureVariable = sender as? ButtonRating else{
return
}
let movieID = gestureVariable.id
print("Hate movie ID ", movieID)
btnSmallOkRate.alpha = 0
btnSmallHateRate.alpha = 0
btnSmallGoodRate.alpha = 0
btnSmallLoveRate.alpha = 0
removeOverlay.alpha = 0
btnRate.alpha = 1
btnRate.image = UIImage(named: "HateIt")
tapHandler?()
}
#objc func goodItem(_ sender: UIButton){
guard let gestureVariable = sender as? ButtonRating else{
return
}
let movieID = gestureVariable.id
print("Good movie ID ", movieID)
btnSmallOkRate.alpha = 0
btnSmallHateRate.alpha = 0
btnSmallGoodRate.alpha = 0
btnSmallLoveRate.alpha = 0
removeOverlay.alpha = 0
btnRate.alpha = 1
btnRate.image = UIImage(named: "goodRate")
tapHandler?()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Cell will dequeueReusableCell after it disapear from scrollview, so u should
set the bool value to reduction.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.btnSmallGoodRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallOkRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallHateRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallLoveRate.id = self.movieObj[indexPath.row].id ?? ""
cell.tapHandler = {
cell.isTap = true
Commons.userGlobalRate += 1
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
if cell.isTap{
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
cell.configure(with: movieObj[indexPath.row])
let ids = ["id" : movieObj[indexPath.row].id!]
excludedDictionary.append(ids)
return cell
}
class MoviesCollectionCell: UICollectionViewCell {
var tapHandler: (()->())?
var isTap = false
}
You need to maintain press action inside "movieObj". Because the cell is always reusable.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.btnSmallGoodRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallOkRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallHateRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallLoveRate.id = self.movieObj[indexPath.row].id ?? ""
cell.tapHandler = {
movieObj[indexPath.row].isTapped = true
Commons.userGlobalRate += 1
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
if movieObj[indexPath.row].isTapped {
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
}
Related
This is the tableViewController with data from firebase, please pay attention that I put number to the images so that to be able to understand which image is displayed in which position.
This is the AudioPlayerViewController, and user comes here when he taps on the first cell in the tableview. The image displays correctly in all cases at first launch.
But when user changes the track right inside of the AudioPlayerViewController pressing next or previous button, the labels are changing accordingly and correctly but the image remains the one which passed on the first launch.
This is the Audio Structure
import Foundation
import UIKit
struct Audio {
let image: UIImage?
let name: String
let albumName: String
let audioImageName: String
let trackURL: String
let time: String
}
This is the AudiosViewModel which fetches data from firebase-firestorm and populates the cells in ViewController.
import Foundation
import FirebaseFirestore
import SDWebImage
class AudiosViewModel: ObservableObject {
#Published var audios = [Audio]()
private var db = Firestore.firestore()
var image: UIImage?
func fetchData() {
db.collection("audios").addSnapshotListener { [self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents")
return
}
self.audios = documents.map { (queryDocumentSnapshot) -> Audio in
let data = queryDocumentSnapshot.data()
let image = image
let name = data["name"] as? String ?? ""
let albumName = data["albumName"] as? String ?? ""
let audioImageName = data["audioImageName"] as? String ?? ""
let trackURL = data["trackURL"] as? String ?? ""
let time = data["time"] as? String ?? ""
return Audio(image: image, name: name, albumName: albumName, audioImageName: audioImageName, trackURL: trackURL, time: time)
}
}
}
}
This is the ViewController with the list of audios fetched from firebase-firestorm
import UIKit
import AVKit
import AVFoundation
import FirebaseFirestore
import Combine
import SDWebImage
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
let placeHolderImage = UIImage(named: "placeHolderImage")
private var viewModel = AudiosViewModel()
private var cancellable: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.fetchData()
self.title = "Audio Lessons"
let nib = UINib(nibName: "AudioCustomTableViewCell", bundle: nil)
table.register(nib, forCellReuseIdentifier: "audioCustomCell")
table.delegate = self
table.dataSource = self
cancellable = viewModel.$audios.sink { _ in
DispatchQueue.main.async{
self.table.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("audios count = ", viewModel.audios.count)
return viewModel.audios.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "audioCustomCell", for: indexPath) as? AudioCustomTableViewCell
let song = viewModel.audios[indexPath.row]
let imageURL = song.audioImageName
cell?.audioImage.sd_imageIndicator = SDWebImageActivityIndicator.gray
cell?.audioImage.sd_setImage(with: URL(string: imageURL),
placeholderImage: placeHolderImage,
options: SDWebImageOptions.highPriority,
context: nil,
progress: nil,
completed: { downloadedImage, downloadException, cacheType, downloadURL in
if let downloadException = downloadException {
print("error downloading the image: \(downloadException.localizedDescription)")
} else {
print("successfuly downloaded the image: \(String(describing: downloadURL?.absoluteString))")
}
self.viewModel.image = cell?.audioImage.image
})
tableView.tableFooterView = UIView()
cell?.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell?.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
cell?.chevronImage?.image = UIImage(systemName: "chevron.compact.right")
cell?.chevronImage?.tintColor = .systemMint
cell?.commonInit(song.time ,song.albumName, song.name, viewModel.image, UIImage(systemName: "chevron.compact.right"))
return cell!
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let position = indexPath.row
let song = viewModel.audios[indexPath.row]
let imageURL = song.audioImageName
SDWebImageManager.shared.loadImage(
with: URL(string: imageURL),
options: .continueInBackground, // or .highPriority
progress: nil,
completed: { [weak self] (image, data, error, cacheType, finished, url) in
guard let self = self else { return }
if error != nil {
// Do something with the error
print("error")
return
}
guard image != nil else {
// No image handle this error
print("error")
return
}
// Do something with image
self.viewModel.image = image
})
guard let vc = storyboard?.instantiateViewController(identifier: "AudioPlayer") as? AudioPlayerViewController else {
return
}
vc.mainImage = viewModel.image
vc.paragraphs = viewModel.audios
vc.position = position
present(vc, animated: true)
}
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? AudioCustomTableViewCell {
cell.cellView.backgroundColor = UIColor(named: "objectHighlightedColor")
cell.mainTitle?.textColor = UIColor(named: "labelHighlighted")
cell.detailTitle?.textColor = UIColor(named: "labelHighlighted")
cell.chevronImage?.tintColor = UIColor(named: "labelHighlighted")
}
}
func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? AudioCustomTableViewCell {
cell.cellView.backgroundColor = UIColor(named: "objectUnHighlightedColor")
cell.mainTitle?.textColor = UIColor(named: "labelUnHighlighted")
cell.detailTitle?.textColor = UIColor(named: "labelUnHighlighted")
cell.chevronImage?.tintColor = .systemMint
}
}
}
This is the AudioPlayerViewController
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
class MyVolumeView: MPVolumeView {
override func volumeSliderRect(forBounds bounds: CGRect) -> CGRect {
// this will avoid the thumb x-offset issue
// while keeping the route button vertically aligned
return bounds.insetBy(dx: 12.0, dy: 0.0).offsetBy(dx: -12.0, dy: -5.0)
}
}
class AudioPlayerViewController: UIViewController {
public var position: Int = 0
public var paragraphs: [Audio] = []
public var mainImage = UIImage(named: "placeHolderImage")
#IBOutlet var holder: UIView!
var player: AVPlayer?
var playerItem: AVPlayerItem?
var isSeekInProgress = false
var chaseTime = CMTime.zero
fileprivate let seekDuration: Float64 = 15
var playerCurrentItemStatus: AVPlayerItem.Status = .unknown
private lazy var audioImageView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFill
v.clipsToBounds = true
v.layer.cornerRadius = 8
return v
}()
private lazy var subjectNameLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.font = .systemFont(ofSize: 14, weight: .bold)
return v
}()
private lazy var lessonNameLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.font = .systemFont(ofSize: 12, weight: .light)
return v
}()
private lazy var progressBar: UISlider = {
let v = UISlider()
v.translatesAutoresizingMaskIntoConstraints = false
v.minimumTrackTintColor = UIColor(named: "subtitleColor")
v.isContinuous = false
return v
}()
private lazy var elapsedTimeLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 14, weight: .light)
v.text = "00:00"
return v
}()
private lazy var remainingTimeLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 14, weight: .light)
v.text = "00:00"
return v
}()
let previousButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
let config = UIImage.SymbolConfiguration(pointSize: 20)
v.setImage(UIImage(systemName: "backward.fill", withConfiguration: config), for: .normal)
v.tintColor = .white
return v
}()
let seekBackwardButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
let config = UIImage.SymbolConfiguration(pointSize: 30)
v.setImage(UIImage(systemName: "gobackward.15", withConfiguration: config), for: .normal)
v.tintColor = .white
return v
}()
let playPauseButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
let config = UIImage.SymbolConfiguration(pointSize: 50)
v.setImage(UIImage(systemName: "pause.fill", withConfiguration: config), for: .normal)
v.tintColor = .white
return v
}()
let seekForwardButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
let config = UIImage.SymbolConfiguration(pointSize: 30)
v.setImage(UIImage(systemName: "goforward.15", withConfiguration: config), for: .normal)
v.tintColor = .white
return v
}()
let nextButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
let config = UIImage.SymbolConfiguration(pointSize: 20)
v.setImage(UIImage(systemName: "forward.fill", withConfiguration: config), for: .normal)
v.tintColor = .white
return v
}()
private lazy var controlStack: UIStackView = {
let v = UIStackView(arrangedSubviews: [previousButton, seekBackwardButton, playPauseButton, seekForwardButton, nextButton])
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.distribution = .equalSpacing
v.spacing = 20
return v
}()
let volumeViewContainer: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let volumeView = MyVolumeView()
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
self.progressBar.addGestureRecognizer(panGesture)
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playback)
}
catch{
print(error)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if holder.subviews.count == 0 {
configure()
}
}
func configure() {
// set up player
let song = paragraphs[position]
let url = URL(string: song.trackURL)
let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
do {
try AVAudioSession.sharedInstance().setMode(.default)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
guard url != nil else {
print("urls string is nil")
return
}
player = AVPlayer(playerItem: playerItem)
let duration : CMTime = playerItem.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
remainingTimeLabel.text = self.stringFromTimeInterval(interval: seconds)
let currentDuration : CMTime = playerItem.currentTime()
let currentSeconds : Float64 = CMTimeGetSeconds(currentDuration)
elapsedTimeLabel.text = self.stringFromTimeInterval(interval: currentSeconds)
progressBar.maximumValue = Float(seconds)
player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if self.player!.currentItem?.status == .readyToPlay {
let time : Float64 = CMTimeGetSeconds(self.player!.currentTime());
self.progressBar.value = Float(time)
self.elapsedTimeLabel.text = self.stringFromTimeInterval(interval: time)
}
let playbackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp
if playbackLikelyToKeepUp == false{
print("IsBuffering")
self.playPauseButton.isHidden = true
} else {
// stop the activity indicator
print("Buffering completed")
self.playPauseButton.isHidden = false
}
}
//subroutine used to keep track of current location of time in audio file
guard let player = player else {
print("player is nil")
return
}
player.play()
}
catch {
print("error accured")
}
holder.applyBlurEffect()
audioImageView.image = mainImage
holder.addSubview(audioImageView)
holder.addSubview(subjectNameLabel)
holder.addSubview(lessonNameLabel)
holder.addSubview(elapsedTimeLabel)
holder.addSubview(remainingTimeLabel)
holder.addSubview(controlStack)
holder.addSubview(progressBar)
holder.addSubview(volumeViewContainer)
volumeViewContainer.addSubview(volumeView)
NSLayoutConstraint.activate([
audioImageView.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
audioImageView.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: -20),
audioImageView.topAnchor.constraint(equalTo: holder.topAnchor, constant: 20),
audioImageView.heightAnchor.constraint(equalToConstant: holder.bounds.width - 40)
])
NSLayoutConstraint.activate([
subjectNameLabel.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
subjectNameLabel.bottomAnchor.constraint(equalTo: lessonNameLabel.topAnchor, constant: -8),
])
NSLayoutConstraint.activate([
lessonNameLabel.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
lessonNameLabel.bottomAnchor.constraint(equalTo: progressBar.topAnchor, constant: -8),
])
NSLayoutConstraint.activate([
progressBar.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
progressBar.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: -20),
progressBar.bottomAnchor.constraint(equalTo: remainingTimeLabel.topAnchor, constant: -3),
])
NSLayoutConstraint.activate([
elapsedTimeLabel.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
elapsedTimeLabel.bottomAnchor.constraint(equalTo: controlStack.topAnchor, constant: -8),
])
NSLayoutConstraint.activate([
remainingTimeLabel.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: -20),
remainingTimeLabel.bottomAnchor.constraint(equalTo: controlStack.topAnchor, constant: -8),
])
NSLayoutConstraint.activate([
controlStack.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 32),
controlStack.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: -32),
controlStack.bottomAnchor.constraint(equalTo: volumeViewContainer.topAnchor, constant: -16),
controlStack.heightAnchor.constraint(equalToConstant: 60)
])
NSLayoutConstraint.activate([
volumeViewContainer.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
volumeViewContainer.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: -20),
volumeViewContainer.bottomAnchor.constraint(equalTo: holder.bottomAnchor, constant: -75),
volumeViewContainer.heightAnchor.constraint(equalToConstant: 30.0),
])
lessonNameLabel.text = song.name
subjectNameLabel.text = song.albumName
progressBar.addTarget(self, action: #selector(progressScrubbed(_:)), for: .valueChanged)
volumeViewContainer.addObserver(self, forKeyPath: "bounds", context: nil)
playPauseButton.addTarget(self, action: #selector(didTapPlayPauseButton), for: .touchUpInside)
previousButton.addTarget(self, action: #selector(didTapBackButton), for: .touchUpInside)
nextButton.addTarget(self, action: #selector(didTapNextButton), for: .touchUpInside)
seekForwardButton.addTarget(self, action: #selector(seekForwardButtonTapped), for: .touchUpInside)
seekBackwardButton.addTarget(self, action: #selector(seekBackwardButtonTapped), for: .touchUpInside)
}
#objc func panGesture(gesture:UIPanGestureRecognizer) {
let currentPoint = gesture.location(in: progressBar)
let percentage = currentPoint.x/progressBar.bounds.size.width;
let delta = Float(percentage) * (progressBar.maximumValue - progressBar.minimumValue)
let value = progressBar.minimumValue + delta
progressBar.setValue(value, animated: true)
}
#objc func progressScrubbed(_ :UISlider) {
let seconds : Int64 = Int64(progressBar.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
player!.seek(to: targetTime)
if player!.rate == 0
{
player?.play()
}
}
func setupNowPlaying() {
// Define Now Playing Info
var nowPlayingInfo = [String : Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "Unstoppable"
if let image = mainImage {
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { size in
return image
}
}
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem?.duration
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func updateNowPlaying(isPause: Bool) {
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo!
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPause ? 0 : 1
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func setupNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self,
selector: #selector(handleInterruption),
name: AVAudioSession.interruptionNotification,
object: nil)
notificationCenter.addObserver(self,
selector: #selector(handleRouteChange),
name: AVAudioSession.routeChangeNotification,
object: nil)
}
#objc func handleRouteChange(notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.headphones {
print("headphones connected")
DispatchQueue.main.sync {
player?.play()
}
break
}
case .oldDeviceUnavailable:
if let previousRoute =
userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
for output in previousRoute.outputs where output.portType == AVAudioSession.Port.headphones {
print("headphones disconnected")
DispatchQueue.main.sync {
player?.pause()
}
break
}
}
default: ()
}
}
#objc func handleInterruption(notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
print("Interruption began")
// Interruption began, take appropriate actions
}
else if type == .ended {
if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
print("Interruption Ended - playback should resume")
player?.play()
} else {
// Interruption Ended - playback should NOT resume
print("Interruption Ended - playback should NOT resume")
}
}
}
}
func stringFromTimeInterval(interval: TimeInterval) -> String {
let interval = Int(interval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
let timeFormatter = NumberFormatter()
timeFormatter.minimumIntegerDigits = 2
timeFormatter.minimumFractionDigits = 0
timeFormatter.roundingMode = .down
guard let minsString = timeFormatter.string(from: NSNumber(value: minutes)),
let secStr = timeFormatter.string(from: NSNumber(value: seconds)) else {
return "00:00"
}
return "\(minsString):\(secStr)"
}
#objc func didTapPlayPauseButton() {
if player?.timeControlStatus == .playing {
//pause
player?.pause()
//show play button
let config = UIImage.SymbolConfiguration(pointSize: 50)
playPauseButton.setImage(UIImage(systemName: "play.fill", withConfiguration: config), for: .normal)
//shrink image
// UIView.animate(withDuration: 0.2, animations: {
// self.audioImageView.frame = CGRect(x: 50,
// y: 50,
// width: self.holder.frame.size.width - 100,
// height: self.holder.frame.size.width - 100)
//
// })
} else {
//play
player?.play()
//show pause button
let config = UIImage.SymbolConfiguration(pointSize: 50)
playPauseButton.setImage(UIImage(systemName: "pause.fill", withConfiguration: config), for: .normal)
//increase image size
// UIView.animate(withDuration: 0.4, animations: {
// self.audioImageView.frame = CGRect(x: 20,
// y: 20,
// width: self.holder.frame.size.width - 40,
// height: self.holder.frame.size.width - 40)
// })
}
}
#objc func didTapBackButton() {
if position > 0 {
position = position - 1
player?.pause()
for subview in holder.subviews {
subview.removeFromSuperview()
}
configure()
}
}
#objc func didTapNextButton() {
if position < (paragraphs.count - 1) {
position = position + 1
player?.pause()
for subview in holder.subviews {
subview.removeFromSuperview()
}
configure()
}
}
#objc func seekBackwardButtonTapped(){
if player == nil { return }
let playerCurrentTime = CMTimeGetSeconds(player!.currentTime())
var newTime = playerCurrentTime - seekDuration
if newTime < 0 { newTime = 0 }
player?.pause()
let selectedTime: CMTime = CMTimeMake(value: Int64(newTime * 1000 as Float64), timescale: 1000)
player?.seek(to: selectedTime)
player?.play()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
player?.play()
UIApplication.shared.isIdleTimerDisabled = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player?.pause()
UIApplication.shared.isIdleTimerDisabled = false
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds" {
// make sure we're getting notified of the MyVolumeView container view
if let cv = object as? UIView,
let mpv = cv.subviews.first as? MyVolumeView {
// set MyVolumeView frame to container view's bounds
// and offset its y-position by 4-points (because of its buggy layout)
mpv.frame = cv.bounds.offsetBy(dx: 0.0, dy: 4.0)
}
}
}
}
I have made my app completely Programatic UI. With iOS 15 I am having a weird issue however with previous iOS versions its working fine as expected. So I have couple of buttons on top of the view, while clicking each button the below table view will be reloaded. When I click on one particular button the table view cell is not showing anything but the space is occupied. At the same time if I press any other button and comeback to the same button everything looks perfect. Kindly check the below code and shed some light.
ModeOfPaymentTableViewCell
class ModeOfPaymentTableViewCell: UITableViewCell{
class var identifier: String { return String.className(self) }
let addressDescriptionTextView:UILabel = {
let textView = UILabel()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textAlignment = .natural
textView.textColor = .gray
textView.font = K.FONT.FONT17
textView.isUserInteractionEnabled = false
textView.numberOfLines = 0
return textView
}()
let otherLabel:UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.textColor = UIColor.black
lbl.textAlignment = .natural
lbl.font = K.FONT.FONT17
return lbl
}()
let indicatorImage:UIImageView = {
let imageview = UIImageView()
imageview.translatesAutoresizingMaskIntoConstraints = false
imageview.image = #imageLiteral(resourceName: "Back")
return imageview
}()
let cardImage : UIImageView = {
let imageview = UIImageView()
imageview.translatesAutoresizingMaskIntoConstraints = false
imageview.contentMode = UIView.ContentMode.scaleAspectFit
return imageview
}()
var primaryData = [String](){
didSet{
setUpDescriptionTextView()
}
}
let shimmer = ShimmerCellWithOutImage()
var showShimmer = Bool(){
didSet{
shimmer.isHidden = !showShimmer
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
shimmer.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(shimmer)
self.addSubview(addressDescriptionTextView)
self.addSubview(otherLabel)
self.addSubview(cardImage)
self.addSubview(indicatorImage)
self.borderColor = UIColor.groupTableViewBackground
self.borderWidth = 1
self.backgroundColor = .white
self.selectionStyle = .none
setUpUI()
if Language.type == "en" || Language.type == "en-SA"{
otherLabel.textAlignment = .right
indicatorImage.image = #imageLiteral(resourceName: "Next")
}
else if Language.type == "ar" || Language.type == "ar-SA" {
otherLabel.textAlignment = .left
indicatorImage.image = #imageLiteral(resourceName: "Back")
}
else{
otherLabel.textAlignment = .right
indicatorImage.image = #imageLiteral(resourceName: "Next")
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("failed to load the frame")
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override public func layoutSubviews() {
super.layoutSubviews()
shimmer.shimmerView.addShimmerAnimation()
}
func setUpDescriptionTextView(){
let leftSideValue = primaryData[0].components(separatedBy: ",")
let attributedText = NSMutableAttributedString()
attributedText.append(NSAttributedString(
string: "\n" + leftSideValue[0], attributes:
[NSAttributedString.Key.foregroundColor :
UIColor(hex:K.COLOR.GRAYTEXTCOLOR),NSAttributedString.Key.font:K.FONT.BOLDFONT12!]))
attributedText.append(NSAttributedString(
string: "\n" + "\(leftSideValue[1])" + "\n", attributes: [NSAttributedString.Key.foregroundColor : UIColor.black,NSAttributedString.Key.font:K.FONT.FONT17!]))
addressDescriptionTextView.attributedText = attributedText
if primaryData[1] != " 0.0 " && primaryData[1] != ""{
// otherLabel.set(image: UIImage.directionIndicator()!, with: primaryData[1])
let data = primaryData[1];
if data == "VISA" || data == "MADA" || data == "MASTER"{
if data == "VISA" {
cardImage.image = #imageLiteral(resourceName: "visa")
}else if data == "MADA"{
cardImage.image = #imageLiteral(resourceName: "mada")
}else if data == "MASTER"{
cardImage.image = #imageLiteral(resourceName: "mascard")
}else{
cardImage.image = #imageLiteral(resourceName: "CARDS")
}
otherLabel.isHidden = true
cardImage.isHidden = false
}else{
otherLabel.text = data
otherLabel.isHidden = false
cardImage.isHidden = true
}
otherLabel.tintColor = UIColor(hex:K.COLOR.BLUE)
}else{
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setUpUI() {
[shimmer.topAnchor.constraint(equalTo: topAnchor,constant: 2),
shimmer.leadingAnchor.constraint(equalTo:leadingAnchor, constant: 0),
shimmer.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
shimmer.bottomAnchor.constraint(equalTo: bottomAnchor,constant:-2)].forEach{$0.isActive = true}
[addressDescriptionTextView.topAnchor.constraint(equalTo: topAnchor,constant: 0),
addressDescriptionTextView.leadingAnchor.constraint(equalTo:leadingAnchor, constant: 10),
addressDescriptionTextView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -UIScreen.main.bounds.width/2),
addressDescriptionTextView.bottomAnchor.constraint(equalTo: bottomAnchor,constant:0)].forEach{$0.isActive = true}
[otherLabel.topAnchor.constraint(equalTo: topAnchor,constant: 0),
otherLabel.leadingAnchor.constraint(equalTo: addressDescriptionTextView.trailingAnchor, constant: 2),
otherLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25),
otherLabel.bottomAnchor.constraint(equalTo: bottomAnchor,constant:0)].forEach{$0.isActive = true}
[cardImage.topAnchor.constraint(equalTo: topAnchor,constant: 0),
// cardImage.leadingAnchor.constraint(equalTo: addressDescriptionTextView.trailingAnchor, constant: 2),
cardImage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25),
cardImage.bottomAnchor.constraint(equalTo: bottomAnchor,constant:0)].forEach{$0.isActive = true}
[indicatorImage.centerYAnchor.constraint(equalTo: centerYAnchor),
indicatorImage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
indicatorImage.heightAnchor.constraint(equalToConstant: 15),
indicatorImage.widthAnchor.constraint(equalToConstant: 10)].forEach{$0.isActive = true}
}
}
EwalletDetailsViewController
Here the issue happens under case Reload:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.separatorStyle = .none
switch selectedType {
case "Activity":
let cell:generalActivityTableViewCell = tableView.dequeueReusableCell(
withIdentifier: generalActivityTableViewCell.identifier,
for: indexPath) as! generalActivityTableViewCell
let date = convertDateStringToFormatedDateString(date: self.activityData?.data?[indexPath.row].date_time ?? "")
cell.activityDescriptionDetails = "\(self.activityData?.data?[indexPath.row].title ?? "") & \(self.activityData?.data?[indexPath.row].desc ?? "")"
cell.activityValueDetails = "\(date) & \(self.activityData?.data?[indexPath.row].amount ?? "")"
return cell
case "Transfer":
if indexPath.row == 1{
if selectedEwalletForTransfer?.ewallet_number ?? "" == ""{
let cell:OtherAccountViewCell = tableView.dequeueReusableCell(
withIdentifier: OtherAccountViewCell.identifier,
for: indexPath) as! OtherAccountViewCell
return cell
}else{
let cell:ModeOfPaymentTableViewCell = tableView.dequeueReusableCell(
withIdentifier: ModeOfPaymentTableViewCell.identifier,
for: indexPath) as! ModeOfPaymentTableViewCell
cell.selectionStyle = .none
cell.showShimmer = false
cell.backgroundColor = UIColor.red
if selectedEwalletForTransfer?.ewallet_title == ""{
cell.primaryData = ["\("To Ewallet Account".localized())"+","+"\(selectedEwalletForTransfer?.ewallet_number ?? "")", "\(selectedEwalletForTransfer?.currency_label ?? "") "+"\(selectedEwalletForTransfer?.balance ?? "") " ]
}else{
cell.primaryData = ["\("To Ewallet Account".localized())"+","+"\(selectedEwalletForTransfer?.ewallet_number ?? "") "+"-"+" \(selectedEwalletForTransfer?.ewallet_title ?? "")" , "\(selectedEwalletForTransfer?.currency_label ?? "") "+"\(selectedEwalletForTransfer?.balance ?? "") " ]
}
return cell
}
}else{
let cell:ReloadPickerCell = tableView.dequeueReusableCell(
withIdentifier: ReloadPickerCell.identifier,
for: indexPath) as! ReloadPickerCell
cell.currencyPicker.delegate = self
cell.currencyPicker.dataSource = self
cell.amountPicker.delegate = self
cell.amountPicker.dataSource = self
cell.amountLabel.isHidden = false
cell.amountLabel.delegate = self
cell.selecteTitleLabel.text = "Enter Amount to transfer".localized()
return cell
}
case "Reload":
if indexPath.row == 1{
let cell:ModeOfPaymentTableViewCell = tableView.dequeueReusableCell(
withIdentifier: ModeOfPaymentTableViewCell.identifier,
for: indexPath) as! ModeOfPaymentTableViewCell
cell.selectionStyle = .none
cell.showShimmer = false
if self.selectedCard != nil{
cell.primaryData = ["\("CARD INFO".localized())"+","+"\(self.selectedCard?.card_masked_number ?? "")", "\(self.selectedCard?.card_brand ?? "")" ]
}else{
cell.primaryData = ["\("CARD INFO".localized())"+","+"Click here to add your credit card".localized(), "" ]
}
cell.layoutSubviews()
return cell
}else{
let cell:ReloadPickerCell = tableView.dequeueReusableCell(
withIdentifier: ReloadPickerCell.identifier,
for: indexPath) as! ReloadPickerCell
cell.currencyPicker.delegate = self
cell.currencyPicker.dataSource = self
cell.amountPicker.delegate = self
cell.amountPicker.dataSource = self
cell.amountLabel.isHidden = true
cell.selecteTitleLabel.text = "ENTER AMOUNT TO RELOAD".localized()
return cell
}
case "Share":
let cell:SharedTableViewCell = tableView.dequeueReusableCell(
withIdentifier: SharedTableViewCell.identifier,
for: indexPath) as! SharedTableViewCell
cell.showShimmer = false
let date = convertDateStringToFormatedDateString(date: self.shareData?.data?[indexPath.row].shared_date ?? "")
cell.primaryData = ["\(self.shareData?.data?[indexPath.row].shared_email ?? "")" + "," + "\(self.shareData?.data?[indexPath.row].user_name ?? "")","\(date)","\(self.shareData?.data?[indexPath.row].profile_pic ?? "")"]
return cell
default:
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
switch selectedType {
case "Activity":
return UITableView.automaticDimension
case "Transfer":
if indexPath.row == 1{
if selectedEwalletForTransfer?.ewallet_number ?? "" == ""{
return 120
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Reload":
if indexPath.row == 1{
if self.cardData?.data?.count == 0{
return UITableView.automaticDimension
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Share":
return UITableView.automaticDimension
default:
return 0
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch selectedType {
case "Activity":
return UITableView.automaticDimension
case "Transfer":
if indexPath.row == 1{
if selectedEwalletForTransfer?.ewallet_number ?? "" == ""{
return 120
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Reload":
if indexPath.row == 1{
if self.cardData?.data?.count == 0 || self.cardData?.data == nil && OPPPaymentProvider.deviceSupportsApplePay() && self.selectedEwallet?.currency_code == "SAR" {
return UITableView.automaticDimension
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Share":
return UITableView.automaticDimension
default:
return 0
}
}
I have been at this for some time now. I can not get my tableView to appear. I think it has something to do with the fact that it is being presented as didMove(toParent)
I am trying to create a page that allows you to add a new Card to the profile. Every time I write it programmatically or use storyboard it crashes as it Unexpectedly found nil while implicitly unwrapping an Optional value.
Here is the view Controller that is presenting the Side Menu
import Foundation
import SideMenu
import FirebaseAuth
import UIKit
import CoreLocation
import SwiftUI
class BeginViewController: UIViewController, MenuControllerDelegate, CLLocationManagerDelegate {
private var sideMenu: SideMenuNavigationController?
struct customData {
var title: String
var image: UIImage
}
let data = [
customData(title: "NottingHill", image: #imageLiteral(resourceName: "norali-nayla-SAhImiWmFaw-unsplash")),
customData(title: "Southall", image: #imageLiteral(resourceName: "alistair-macrobert-8wMflrTLm2g-unsplash")),
customData(title: "Tower Hill", image: #imageLiteral(resourceName: "peregrine-communications-0OLnnZWg860-unsplash")),
customData(title: "Mansion House", image: #imageLiteral(resourceName: "adam-birkett-cndNklOnHO4-unsplash")),
customData(title: "Westminster", image: #imageLiteral(resourceName: "simon-mumenthaler-NykjYbCW6Z0-unsplash")),
customData(title: "London Bridge", image: #imageLiteral(resourceName: "hert-niks-CjouXgWrTRk-unsplash"))
]
struct Constants {
static let cornerRadius: CGFloat = 15.0 }
let manager = CLLocationManager()
private let ProfileController = ProfileViewController()
private let MyBookingsController = MyBookingsViewController()
private let WalletController = WalletViewController()
private let FAQController = FAQViewController()
private let SettingsController = SettingsViewController()
#IBOutlet weak var StoreButton: UIButton!
#IBOutlet weak var DeliverButton: UIButton!
#IBOutlet weak var AirportButton: UIButton!
#IBOutlet weak var HotelsButton: UIButton!
fileprivate let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(customCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
// buttons
StoreButton.layer.cornerRadius = Constants.cornerRadius
StoreButton.layer.shadowOffset = .zero
StoreButton.layer.shadowOpacity = 0.3
StoreButton.layer.shadowColor = UIColor.black.cgColor
StoreButton.layer.shadowRadius = 5
DeliverButton.layer.cornerRadius = Constants.cornerRadius
DeliverButton.layer.shadowOffset = .zero
DeliverButton.layer.shadowOpacity = 0.3
DeliverButton.layer.shadowColor = UIColor.black.cgColor
DeliverButton.layer.shadowRadius = 5
AirportButton.layer.cornerRadius = Constants.cornerRadius
AirportButton.layer.shadowOffset = .zero
AirportButton.layer.shadowOpacity = 0.3
AirportButton.layer.shadowColor = UIColor.black.cgColor
AirportButton.layer.shadowRadius = 5
HotelsButton.layer.cornerRadius = Constants.cornerRadius
HotelsButton.layer.shadowOffset = .zero
HotelsButton.layer.shadowOpacity = 0.3
HotelsButton.layer.shadowColor = UIColor.black.cgColor
HotelsButton.layer.shadowRadius = 5
//CollectionViewNearbyPlaces
view.addSubview(collectionView)
collectionView.backgroundColor = .clear
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 450).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 25).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.5).isActive = true
collectionView.delegate = self
collectionView.dataSource = self
// title
title = "handl"
//background
let menu = MenuController(with: [ "Home", "Profile", "My Bookings",
"Wallet",
"FAQ","Settings"])
menu.delegate = self
sideMenu = SideMenuNavigationController(rootViewController: menu)
sideMenu?.leftSide = true
sideMenu?.setNavigationBarHidden(true, animated: false)
SideMenuManager.default.leftMenuNavigationController = sideMenu
SideMenuManager.default.addPanGestureToPresent(toView: view)
addChildControllers()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
handleNotAuthenticated()
}
private func addChildControllers() {
addChild(ProfileController)
addChild(MyBookingsController)
addChild(WalletController)
addChild(FAQController)
addChild(SettingsController)
view.addSubview(ProfileController.view)
view.addSubview(MyBookingsController.view)
view.addSubview(WalletController.view)
view.addSubview(FAQController.view)
view.addSubview(SettingsController.view)
ProfileController.view.frame = view.bounds
MyBookingsController.view.frame = view.bounds
WalletController.view.frame = view.bounds
FAQController.view.frame = view.bounds
SettingsController.view.frame = view.bounds
ProfileController.didMove(toParent: self)
MyBookingsController.didMove(toParent: self)
WalletController.didMove(toParent: self)
FAQController.didMove(toParent: self)
SettingsController.didMove(toParent: self)
ProfileController.view.isHidden = true
MyBookingsController.view.isHidden = true
WalletController.view.isHidden = true
FAQController.view.isHidden = true
SettingsController.view.isHidden = true
}
#IBAction func SideMenuButton(_ sender: Any) {
present(sideMenu!, animated: true)
}
func didSelectMenuItem(named: String) {
sideMenu?.dismiss(animated: true, completion: { [weak self] in
self?.title = named
if named == "Home" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
if named == "Profile" {
self?.ProfileController.view.isHidden = false
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
if named == "My Bookings" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = false
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
else if named == "Wallet" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = false
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
else if named == "FAQ" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = false
self?.SettingsController.view.isHidden = true
}
else if named == "Settings" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = false
}
})
}
}
extension BeginViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width/2.5, height: collectionView.frame.width/2)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! customCell
cell.data = self.data[indexPath.row]
return cell
}
class customCell: UICollectionViewCell {
var data: customData? {
didSet {
guard let data = data else { return }
bg.image = data.image
}
}
fileprivate let bg: UIImageView = {
let iv = UIImageView()
iv.image = #imageLiteral(resourceName: "adam-birkett-cndNklOnHO4-unsplash")
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.layer.shadowColor = UIColor.black.cgColor
iv.layer.shadowOpacity = 1
iv.layer.shadowOffset = CGSize.zero
iv.layer.shadowRadius = 10
iv.layer.shadowPath = UIBezierPath(rect: iv.bounds).cgPath
iv.layer.shouldRasterize = false
iv.layer.cornerRadius = 10
iv.clipsToBounds = true
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(bg)
bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//if user is not logged in show login
private func handleNotAuthenticated() {
//check auth status
if Auth.auth().currentUser == nil {
//show log in screen
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .fullScreen
present(loginVC, animated: false)
}
}
}
and Here is the viewController I am trying to present a tableView on. It comes up with a white screen, but no tableView. Nor are my navigation items showing. Even when written programmatically.
import UIKit
class WalletViewController: UIViewController {
var addNewCard = [String]()
let button = UIButton()
let tableView = UITableView()
// MARK: - Properties
override func viewDidLoad() {
super.viewDidLoad()
addTable()
view.backgroundColor = UIColor(named: "RED")
button.setTitle("Add New Card", for: .normal)
view.addSubview(button)
button.backgroundColor = UIColor(named: "yellow-2")
button.setTitleColor(UIColor(named: "RED"), for: .normal)
button.frame = CGRect(x: 25, y: 700, width: 350, height: 50)
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(didTapAddButton), for: .touchUpInside)
if !UserDefaults().bool(forKey: "setup") {
UserDefaults().set(true, forKey: "setup")
UserDefaults().set(0, forKey: "count")
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add New Card", style: .plain, target: self,
action: #selector(didTapAdd))
}
}
func updateCard() {
guard let count = UserDefaults().value(forKey: "count") as? Int else {
return
}
for x in 0..<count {
if let addCard = UserDefaults().value(forKey: "addCard\(x+1)") as? String {
addNewCard.append(addCard)
}
}
}
func addTable() {
tableView.frame = view.bounds
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .singleLine
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "addCard")
self.view.addSubview(tableView)
}
#IBAction func didTapAdd() {
let vc = storyboard?.instantiateViewController(withIdentifier: "addCard") as! addCardViewController
vc.update = {
DispatchQueue.main.async {
self.updateCard()
}
}
navigationController?.pushViewController(vc, animated: true)
}
#objc private func didTapAddButton() {
let rootVC = addCardViewController()
let navVC = UINavigationController(rootViewController: rootVC)
navVC.modalPresentationStyle = .fullScreen
present(navVC, animated: true)
}
}
extension WalletViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
extension WalletViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let add = tableView.dequeueReusableCell(withIdentifier: "addCard", for: indexPath)
add.textLabel?.text = addNewCard[indexPath.row]
return add
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return addNewCard.count
}
}
My problem is that UITableView lags quite a lot while scrolling.
This is what I am trying to achieve
Starting from the top I have a simple section header with only one checkbox and one UILabel. Under this header, you can see a custom cell with only one UILabel aligned to the center. This custom cell works like another header for the data that would be shown below (Basically a 3D array). Under these "headers" are custom cells that contain one multiline UILabel and under this label is a container for a variable amount of lines containing a checkbox and an UILabel. On the right side of the cell is also a button (blue/white arrow).
So this means the content is shown like this:
Section header (containing day and date)
Custom UITableViewCell = header (containing some header information)
Custom UITableViewCell (containing data to be shown)
Here is my code:
cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let (isHeader, headerNumber, semiResult) = checkIfIsHeader(section: indexPath.section, row: indexPath.row)
let row = indexPath.row
if isHeader {
let chod = objednavkaDny[indexPath.section].chody[headerNumber+1]
let cell = tableView.dequeueReusableCell(withIdentifier: cellHeaderReuseIdentifier, for: indexPath) as! ObjednavkyHeaderTableViewCell
cell.titleLabel.text = chod.popisPoradiJidla
cell.selectionStyle = .none
return cell
}else{
let chod = objednavkaDny[indexPath.section].chody[headerNumber]
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ObjednavkyTableViewCell
cell.updateData(objednavka: chod.objednavky[row-semiResult], canSetAmount: self.typDialogu == 3)
return cell
}
}
checkIfIsHeader:
func checkIfIsHeader(section: Int, row: Int) -> (Bool, Int, Int){
if let cachedResult = checkIfHeaderCache[section]?[row] {
return (cachedResult[0] == 1, cachedResult[1], cachedResult[2])
}
var isHeader = false
var semiResult = 0
var headerNumber = -1
for (index, chod) in objednavkaDny[section].chody.enumerated() {
let sum = chod.objednavky.count
if row == semiResult {
isHeader = true
break
}else if row < semiResult {
semiResult -= objednavkaDny[section].chody[index-1].objednavky.count
break
}else {
headerNumber += 1
semiResult += 1
if index != objednavkaDny[section].chody.count - 1 {
semiResult += sum
}
}
}
checkIfHeaderCache[section] = [Int:[Int]]()
checkIfHeaderCache[section]![row] = [isHeader ? 1 : 0, headerNumber, semiResult]
return (isHeader, headerNumber, semiResult)
}
and the main cell that shows the data:
class ObjednavkyTableViewCell: UITableViewCell {
lazy var numberTextField: ObjednavkyTextField = {
let textField = ObjednavkyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
let mealLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .black
label.textAlignment = .left
label.font = UIFont(name: ".SFUIText", size: 15)
label.numberOfLines = 0
label.backgroundColor = .white
label.isOpaque = true
return label
}()
lazy var detailsButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "arrow-right")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.imageView?.tintColor = UIColor.custom.blue.classicBlue
button.imageView?.contentMode = .scaleAspectFit
button.contentHorizontalAlignment = .right
button.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 0)
button.addTarget(self, action: #selector(detailsButtonPressed), for: .touchUpInside)
button.backgroundColor = .white
button.isOpaque = true
return button
}()
let pricesContainerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.isOpaque = true
return view
}()
var canSetAmount = false {
didSet {
canSetAmount ? showNumberTextField() : hideNumberTextField()
}
}
var shouldShowPrices = false {
didSet {
shouldShowPrices ? showPricesContainerView() : hidePricesContainerView()
}
}
var pricesContainerHeight: CGFloat = 0
private let priceViewHeight: CGFloat = 30
var mealLabelLeadingConstraint: NSLayoutConstraint?
var mealLabelBottomConstraint: NSLayoutConstraint?
var pricesContainerViewHeightConstraint: NSLayoutConstraint?
var pricesContainerViewBottomConstraint: NSLayoutConstraint?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func detailsButtonPressed() {
}
func updateData(objednavka: Objednavka, canSetAmount: Bool) {
self.canSetAmount = canSetAmount
if let popisJidla = objednavka.popisJidla, popisJidla != "", popisJidla != " " {
self.mealLabel.text = popisJidla
}else{
self.mealLabel.text = objednavka.nazevJidelnicku
}
if objednavka.objects.count > 1 {
shouldShowPrices = true
setPricesStackView(with: objednavka.objects)
checkIfSelected(objects: objednavka.objects)
}else{
shouldShowPrices = false
self.numberTextField.text = String(objednavka.objects[0].pocet)
//setSelected(objednavka.objects[0].pocet > 0, animated: false)
objednavka.objects[0].pocet > 0 ? setSelectedStyle() : setDeselectedStyle()
}
}
//---------------
func checkIfSelected(objects: [ObjednavkaObject]) {
var didChangeSelection = false
for object in objects { // Checks wether cell should be selected or not
if object.pocet > 0 {
setSelected(true, animated: false)
setSelectedStyle()
didChangeSelection = true
break
}
}
if !didChangeSelection {
setSelected(false, animated: false)
setDeselectedStyle()
}
}
//--------------
func showNumberTextField() {
numberTextField.isHidden = false
mealLabelLeadingConstraint?.isActive = false
mealLabelLeadingConstraint = mealLabel.leadingAnchor.constraint(equalTo: numberTextField.trailingAnchor, constant: 10)
mealLabelLeadingConstraint?.isActive = true
}
func hideNumberTextField() {
numberTextField.isHidden = true
mealLabelLeadingConstraint?.isActive = false
mealLabelLeadingConstraint = mealLabel.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor, constant: 0)
mealLabelLeadingConstraint?.isActive = true
}
func showPricesContainerView() {
hideNumberTextField()
pricesContainerView.isHidden = false
mealLabelBottomConstraint?.isActive = false
pricesContainerViewBottomConstraint?.isActive = true
}
func hidePricesContainerView() {
pricesContainerView.isHidden = true
pricesContainerViewBottomConstraint?.isActive = false
mealLabelBottomConstraint?.isActive = true
}
//--------------
func setSelectedStyle() {
self.backgroundColor = UIColor.custom.blue.classicBlue
mealLabel.textColor = .white
mealLabel.backgroundColor = UIColor.custom.blue.classicBlue
for subview in pricesContainerView.subviews where subview is ObjednavkyPriceView {
let priceView = (subview as! ObjednavkyPriceView)
priceView.titleLabel.textColor = .white
priceView.checkBox.backgroundColor = UIColor.custom.blue.classicBlue
priceView.titleLabel.backgroundColor = UIColor.custom.blue.classicBlue
priceView.backgroundColor = UIColor.custom.blue.classicBlue
}
pricesContainerView.backgroundColor = UIColor.custom.blue.classicBlue
detailsButton.imageView?.tintColor = .white
detailsButton.backgroundColor = UIColor.custom.blue.classicBlue
}
func setDeselectedStyle() {
self.backgroundColor = .white
mealLabel.textColor = .black
mealLabel.backgroundColor = .white
for subview in pricesContainerView.subviews where subview is ObjednavkyPriceView {
let priceView = (subview as! ObjednavkyPriceView)
priceView.titleLabel.textColor = .black
priceView.checkBox.backgroundColor = .white
priceView.titleLabel.backgroundColor = .white
priceView.backgroundColor = .white
}
pricesContainerView.backgroundColor = .white
detailsButton.imageView?.tintColor = UIColor.custom.blue.classicBlue
detailsButton.backgroundColor = .white
}
//-----------------
func setPricesStackView(with objects: [ObjednavkaObject]) {
let subviews = pricesContainerView.subviews
var subviewsToDelete = subviews.count
for (index, object) in objects.enumerated() {
subviewsToDelete -= 1
if subviews.count - 1 >= index {
let priceView = subviews[index] as! ObjednavkyPriceView
priceView.titleLabel.text = object.popisProduktu // + " " + NSNumber(value: object.cena).getFormattedString(currencySymbol: "Kč") // TODO: currencySymbol
priceView.canSetAmount = canSetAmount
priceView.count = object.pocet
priceView.canOrder = (object.nelzeObj == nil || object.nelzeObj == "")
}else {
let priceView = ObjednavkyPriceView(frame: CGRect(x: 0, y: CGFloat(index) * priceViewHeight + CGFloat(index * 5), width: pricesContainerView.frame.width, height: priceViewHeight))
pricesContainerView.addSubview(priceView)
priceView.titleLabel.text = object.popisProduktu // + " " + NSNumber(value: object.cena).getFormattedString(currencySymbol: "Kč") // TODO: currencySymbol
priceView.numberTextField.delegate = self
priceView.canSetAmount = canSetAmount
priceView.canOrder = (object.nelzeObj == nil || object.nelzeObj == "")
priceView.count = object.pocet
pricesContainerHeight += ((index == 0) ? 30 : 35)
}
}
if subviewsToDelete > 0 { // Deletes unwanted subviews
for _ in 0..<subviewsToDelete {
pricesContainerView.subviews.last?.removeFromSuperview()
pricesContainerHeight -= pricesContainerHeight + 5
}
}
if pricesContainerHeight < 0 {
pricesContainerHeight = 0
}
pricesContainerViewHeightConstraint?.constant = pricesContainerHeight
}
func setupView() {
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
self.backgroundColor = .white
contentView.addSubview(numberTextField)
contentView.addSubview(mealLabel)
contentView.addSubview(detailsButton)
contentView.addSubview(pricesContainerView)
setupConstraints()
}
func setupConstraints() {
numberTextField.anchor(leading: readableContentGuide.leadingAnchor, size: CGSize(width: 30, height: 30))
numberTextField.centerYAnchor.constraint(equalTo: mealLabel.centerYAnchor).isActive = true
detailsButton.anchor(trailing: readableContentGuide.trailingAnchor, size: CGSize(width: 30, height: 30))
detailsButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
mealLabel.anchor(top: contentView.topAnchor, trailing: detailsButton.leadingAnchor, padding: .init(top: 10, left: 0, bottom: 0, right: -10))
mealLabelBottomConstraint = mealLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
mealLabelBottomConstraint?.priority = UILayoutPriority(rawValue: 999)
pricesContainerView.anchor(top: mealLabel.bottomAnchor, leading: readableContentGuide.leadingAnchor, trailing: detailsButton.leadingAnchor, padding: .init(top: 10, left: 0, bottom: 0, right: -10))
pricesContainerViewBottomConstraint = pricesContainerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
pricesContainerViewBottomConstraint?.priority = UILayoutPriority(rawValue: 999)
pricesContainerViewHeightConstraint = pricesContainerView.heightAnchor.constraint(equalToConstant: 0)
pricesContainerViewHeightConstraint?.priority = UILayoutPriority(rawValue: 999)
pricesContainerViewHeightConstraint?.isActive = true
}
}
To conclude how it is done:
tableView.rowHeight is set to UITableViewAutomaticDymension
inside cellForRowAt I get the data from an array and give it to the
cell
all the cells are set up in code using constraints
all the views have set isOpaque = true
heights of the cells are cached
cells are set to rasterize
I also noticed that it lags at certain scroll levels and that sometimes it works just fine and sometimes it lags a lot.
Despite all of the optimization I have done, the tableView still lags while scrolling.
Here is a screenshot from Instruments
Any tip how to improve the scrolling performance is highly appreciated!
(I might have forgotten to include some code/information so feel free to ask me in the comments.)
I can't tell you where the lag happens exactly but when we are talking about lagging during scrolling, it's related to your cellForRowAt delegate method. What happends is that too many things are going on within this method and it's called for every cells that are displaying & going to display. I see that your are trying to cache the result by checkIfHeaderCache but still, there is a for loop at the very beginning to determine header cell.
Suggestions:
I don't know where you get data (objednavkaDny) from but right after you get the data, do a full loop through and determin cell type one by one, and save the result some where base on your design. During this loading time, you can show some loading message on the screen. Then, within the cellForRow method, you should be just simply using things like
if (isHeader) {
render header cell
} else {
render other cell
}
Bottom line:
cellForRow method is not designed to handle heavy calculations, and it will slow down the scrolling if you do so. This method is for assigning values to the cached table view cell only and that's the only thing it is good at.
I am creating a dynamic uilabel in CellforRowat for particuklar index i.e indexpath.row=0
But When I Scroll down I am seeing that UI Label on 4th Index as well ....
Why is it coming on 3rd index if I have already added a condition in Cellforroat as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// var cell:ActivityMainTableViewCell?
if(CheckFlag == false){
//CheckFlag = true
}
else{
CheckFlag = false
}
// cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! as? ActivityMainTableViewCell
let cell:ActivityMainTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! ActivityMainTableViewCell
/// let cell = tableView.cellForRow(at: indexPath) as! ActivityMainTableViewCell
///let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ActivityMainTableViewCell
/// cell.viewheight.constant = 0
cell.imgViewFirst.layer.cornerRadius = (cell.imgViewFirst.frame.size.width) / 2
cell.imgViewFirst.layer.borderWidth = 3.0
cell.imgViewFirst.layer.borderColor = UIColor.white.cgColor
cell.imgViewFirst.clipsToBounds = true
cell.ImageViewThird.layer.cornerRadius = (cell.ImageViewThird.frame.size.width) / 2
cell.ImageViewThird.layer.borderWidth = 3.0
cell.ImageViewThird.layer.borderColor = UIColor.white.cgColor
cell.ImageViewThird.clipsToBounds = true
cell.ImageViewSecond.layer.cornerRadius = (cell.ImageViewSecond.frame.size.width) / 2
cell.ImageViewSecond.layer.borderWidth = 3.0
cell.ImageViewSecond.layer.borderColor = UIColor.white.cgColor
cell.ImageViewSecond.clipsToBounds = true
print(indexPath)
print(indexPath.row)
print(dicArray)
cell.DateOfMeeting.text = dicArray[indexPath.row]["Date"] as? String
cell.CompanyInMeetiong.text = dicArray[indexPath.row]["Company"] as? String
cell.lblNews.text = dicArray[indexPath.row]["News"] as? String
cell.lblCommonKey.text = dicArray[indexPath.row]["CommonKey"] as? String
print(SectordicArray)
cell.imgViewFirst.isHidden = true
cell.lblCommonKey.isHidden = true
cell.btnOthers.tag = indexPath.row
//for i in 0..<section.count {
print(cell.lblCommonKey.text)
print(indexPath.row)
RowHeight = 110.0
CellHeight = 140.0
isFirstTime = true
// print(AllCompanydicArray)
if(indexPath.row == 0){
let Data = AllCompanydicArray[indexPath.row]
print(Data)
for i in 0..<Data.count {
print(Data[i])
if(isFirstTime == false){
RowHeight = RowHeight + 30.0
CellHeight = CellHeight + 30.0
}
else{
isFirstTime = false
}
let Company = (Data[i] as NSDictionary).value(forKey: "Company") as! [String]
let Contacts = (Data[i] as NSDictionary).value(forKey: "Participants") as! [String]
// section.append(Company)
//items.append(Contacts)
for k in 0..<Company.count {
let newLabel = UILabel(frame: CGRect(x: 10.0, y: RowHeight, width:cell.frame.width, height: 30.0))
newLabel.text = Company[k]
newLabel.font = UIFont(name:"HelveticaNeue-Bold", size: 13)
cell.addSubview(newLabel)
}
for j in 0..<Contacts.count {
RowHeight = RowHeight + 30.0
CellHeight = CellHeight + 30.0
var
newLabel = UILabel(frame: CGRect(x: 10.0, y: RowHeight, width:30, height: 30.0))
newLabel.text = CaptionName(Name: Contacts[j])
//"RC"
newLabel.font = UIFont(name:"HelveticaNeue-Bold", size: 11)
newLabel.textAlignment = .center
newLabel.backgroundColor = getColorByHex(rgbHexValue: 0x002366, alpha: 0.9)// UIColor(red: 0.0, green: 14.0, blue: 40.0, alpha: 100.0)
newLabel.layer.cornerRadius = (newLabel.frame.size.width) / 2
newLabel.layer.borderWidth = 1.0
newLabel.clipsToBounds = true
newLabel.textColor = UIColor.white
newLabel.layer.borderColor = UIColor.white.cgColor
cell.addSubview(newLabel)
newLabel = UILabel(frame: CGRect(x: 50, y: RowHeight, width:cell.frame.width-30, height: 30.0))
newLabel.text = Contacts[j]//"Raghav Chopra"
newLabel.font = UIFont(name:"HelveticaNeue", size: 12)
cell.addSubview(newLabel)
}
}
}
else{
}
return cell
}
Please let me know how can i avoid uilabel on 4th index.