My app downloads images from firebase and displays them in the tableViewController cells, also I need the AudioPlayerViewController to display exactly same image of the selected row/cell. For now in anyway it displays only the last downloaded image. I tried to save downloaded images into array but it always threw errors like cannot assign UIImage to [UIImage] or something like this. Hope to your help friends.
import UIKit
import AVKit
import AVFoundation
import FirebaseFirestore
import Combine
import SDWebImage
class ListOfAudioLessonsTableViewController: 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?.commonInit(song.albumName, song.name, viewModel.image)
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
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) {
cell.contentView.backgroundColor = UIColor(named: "AudioLessonsHighlighted")
cell.textLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
cell.detailTextLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
}
}
func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
}
This is the viewModel
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 paragraphNumber = data["paragraphNumber"] as? String ?? ""
let trackURL = data["trackURL"] as? String ?? ""
return Audio(image: image, name: name, albumName: albumName, paragraphNumber: paragraphNumber, audioImageName: audioImageName, trackURL: trackURL)
}
}
}
}
this is the AudioPlayerViewController
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
import Combine
class AudioPlayerViewController: UIViewController {
// private var viewModel = AudiosViewModel()
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
// User Interface elements
private let albumImageView: UIImageView = {
let imageView = UIImageView()
imageView.layer.shadowOpacity = 0.3
imageView.layer.shadowRadius = 3
imageView.layer.shadowOffset = CGSize(width: 6, height: 6)
imageView.layer.shadowRadius = 8
imageView.contentMode = .scaleAspectFill
return imageView
}()
private let paragraphNumberLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 16, weight: .light)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
private let albumNameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 18, weight: .bold)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
private let songNameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 16, weight: .ultraLight)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
private let elapsedTimeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 12, weight: .light)
label.textColor = UIColor(named: "PlayerColors")
label.text = "00:00"
label.numberOfLines = 0
return label
}()
private let remainingTimeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 12, weight: .light)
label.textColor = UIColor(named: "PlayerColors")
label.text = "00:00"
label.numberOfLines = 0
return label
}()
private let playbackSlider: UISlider = {
let v = UISlider()
v.addTarget(AudioPlayerViewController.self, action: #selector(progressScrubbed(_:)), for: .valueChanged)
v.minimumTrackTintColor = UIColor.lightGray
v.maximumTrackTintColor = UIColor.darkGray
v.thumbTintColor = UIColor(named: "PlayerColors")
v.minimumValue = 0
v.isContinuous = false
return v
}()
let playPauseButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
self.playbackSlider.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)
playbackSlider.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.playbackSlider.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
}
}
playbackSlider.addTarget(self, action: #selector(AudioPlayerViewController.progressScrubbed(_:)), for: .valueChanged)
self.view.addSubview(playbackSlider)
//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")
}
// set up user interface elements
//album cover
albumImageView.frame = CGRect(x: 20,
y: 20,
width: holder.frame.size.width - 40,
height: holder.frame.size.width - 40)
albumImageView.image = mainImage
holder.addSubview(albumImageView)
//Labels Song name, album, artist
albumNameLabel.frame = CGRect(x: 20,
y: holder.frame.size.height - 300,
width: holder.frame.size.width - 40,
height: 20)
paragraphNumberLabel.frame = CGRect(x: 20,
y: holder.frame.size.height - 280,
width: holder.frame.size.width-40,
height: 20)
songNameLabel.frame = CGRect(x: 20,
y: holder.frame.size.height - 260,
width: holder.frame.size.width-40,
height: 20)
playbackSlider.frame = CGRect(x: 20,
y: holder.frame.size.height - 235,
width: holder.frame.size.width-40,
height: 40)
elapsedTimeLabel.frame = CGRect(x: 25,
y: holder.frame.size.height - 200,
width: holder.frame.size.width-40,
height: 15)
remainingTimeLabel.frame = CGRect(x: holder.frame.size.width-60,
y: holder.frame.size.height - 200,
width: holder.frame.size.width-20,
height: 15)
songNameLabel.text = song.name
albumNameLabel.text = song.albumName
paragraphNumberLabel.text = song.paragraphNumber
holder.addSubview(songNameLabel)
holder.addSubview(albumNameLabel)
holder.addSubview(paragraphNumberLabel)
holder.addSubview(elapsedTimeLabel)
holder.addSubview(remainingTimeLabel)
//Player controls
let nextButton = UIButton()
let backButton = UIButton()
let seekForwardButton = UIButton()
let seekBackwardButton = UIButton()
//frames of buttons
playPauseButton.frame = CGRect(x: (holder.frame.size.width - 40) / 2.0,
y: holder.frame.size.height - 172.5,
width: 40,
height: 40)
nextButton.frame = CGRect(x: holder.frame.size.width - 70,
y: holder.frame.size.height - 162.5,
width: 30,
height: 20)
backButton.frame = CGRect(x: 70 - 30,
y: holder.frame.size.height - 162.5,
width: 30,
height: 20)
seekForwardButton.frame = CGRect(x: holder.frame.size.width - 140,
y: holder.frame.size.height - 167.5,
width: 30,
height: 30)
seekBackwardButton.frame = CGRect(x: 110,
y: holder.frame.size.height - 167.5,
width: 30,
height: 30)
let volumeView = MPVolumeView(frame: CGRect(x: 20,
y: holder.frame.size.height - 80,
width: holder.frame.size.width-40,
height: 30))
holder.addSubview(volumeView)
//actions of buttons
playPauseButton.addTarget(self, action: #selector(didTapPlayPauseButton), for: .touchUpInside)
backButton.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)
//styling of buttons
playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
nextButton.setBackgroundImage(UIImage(systemName: "forward.fill"), for: .normal)
backButton.setBackgroundImage(UIImage(systemName: "backward.fill"), for: .normal)
seekForwardButton.setBackgroundImage(UIImage(systemName: "goforward.15"), for: .normal)
seekBackwardButton.setBackgroundImage(UIImage(systemName: "gobackward.15"), for: .normal)
playPauseButton.tintColor = UIColor(named: "PlayerColors")
nextButton.tintColor = UIColor(named: "PlayerColors")
backButton.tintColor = UIColor(named: "PlayerColors")
seekForwardButton.tintColor = UIColor(named: "PlayerColors")
seekBackwardButton.tintColor = UIColor(named: "PlayerColors")
holder.addSubview(playPauseButton)
holder.addSubview(nextButton)
holder.addSubview(backButton)
holder.addSubview(seekForwardButton)
holder.addSubview(seekBackwardButton)
}
#objc func panGesture(gesture: UIPanGestureRecognizer) {
let currentPoint = gesture.location(in: playbackSlider)
let percentage = currentPoint.x/playbackSlider.bounds.size.width;
let delta = Float(percentage) * (playbackSlider.maximumValue - playbackSlider.minimumValue)
let value = playbackSlider.minimumValue + delta
playbackSlider.setValue(value, animated: true)
}
#objc func progressScrubbed(_ playbackSlider: UISlider!) {
let seconds : Int64 = Int64(playbackSlider.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 = UIImage(named: "artist") {
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { size in
return image
}
}
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem?.duration
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate
// Set the metadata
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func updateNowPlaying(isPause: Bool) {
// Define Now Playing Info
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo!
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPause ? 0 : 1
// Set the metadata
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")
}
}
}
}
#objc func didTapPlayPauseButton() {
if player?.timeControlStatus == .playing {
//pause
player?.pause()
//show play button
playPauseButton.setBackgroundImage(UIImage(systemName: "play.fill"), for: .normal)
//shrink image
UIView.animate(withDuration: 0.2, animations: {
self.albumImageView.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
playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
//increase image size
UIView.animate(withDuration: 0.4, animations: {
self.albumImageView.frame = CGRect(x: 20,
y: 20,
width: self.holder.frame.size.width - 40,
height: self.holder.frame.size.width - 40)
})
}
}
This is Audio Struct
import Foundation
import UIKit
struct Audio {
let image: UIImage?
let name: String
let albumName: String
let paragraphNumber: String
let audioImageName: String
let trackURL: String
}
This is customTableViewCell
import UIKit
class AudioCustomTableViewCell: UITableViewCell {
#IBOutlet weak var cellView: UIView!
#IBOutlet weak var audioImage: UIImageView!
#IBOutlet weak var mainTitle: UILabel!
#IBOutlet weak var detailTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setCardView(toView: audioImage)
setCardView(toView: cellView)
self.audioImage.layer.cornerRadius = 6
audioImage.clipsToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func commonInit(_ title: String, _ subtitle: String, _ image: UIImage?){
mainTitle.text = title
detailTitle.text = subtitle
audioImage.image = image
}
func setCardView(toView: UIView) {
toView.layer.shadowColor = UIColor.black.cgColor
toView.layer.shadowOpacity = 0.3
toView.layer.shadowRadius = 3
toView.layer.shadowOffset = CGSize(width: 6, height: 6)
toView.layer.shadowRadius = 8
toView.layer.cornerRadius = 6
}
}
when you are navigating from didSelectRowAt Indexpath please pass all the values to your detailViewController when you navigate to that view all value will be there to use.
use let song = viewModel.audios[indexPath.row]
let imageURL = song.audioImageName
Get values and pass it to your detailViewController
do the same thing in didSelectRowAt Indexpath what you did in cellForRowAt indexPath because there you are just passing position and taking values from model and passing it that's why this might be happening.
if you are getting only this error cannot assign UIImage to [UIImage]
then you can just convert your UIImage to [UIImage] and assign it to your [UIImage]
This line causes the bug, viewModel.image will always be the last download one.
self.viewModel.image = cell?.audioImage.image
you can simply pass the image URL to the next VC
vc.mainImage = viewModel.audios[indexPath.row].audioImageName
then use SDWebImage to load this image from the url
I tried the other method of SDWebImage and in didselectRowAt according to suggestion of Rahul Nimje. Thanks to him it worked. Dear Rahul, how will it affect on weight of the app, is it the correct way?
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 let err = error {
// Do something with the error
return
}
guard let img = image else {
// No image handle this error
return
}
// Do something with image
self.viewModel.image = image
})
enter image description hereThere is a stretch effect.But the additional header is duplicated , only without data.Can anyone correct my code that I wrote extra or incorrectly called the header?.I have custom Header xib. link tutorial https://mobikul.com/table-view-stretch-header-parallax-animation-in-swift/
My code:
var headerView: CustomSoccerHeaderView? = {
return Bundle.main.loadNibNamed("CustomSoccerHeaderView", owner: nil, options: nil)!.first as! CustomSoccerHeaderView
}()
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem()
backButton.title = ""
self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton
// self.navigationItem.rightBarButtonItem = barButton
// self.navigationItem.rightBarButtonItem?.tintColor = UIColor.universalColorYellow
let yourBackImage = UIImage(named: "backItem")
self.navigationController?.navigationBar.backIndicatorImage = yourBackImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
self.navigationController?.navigationItem.leftItemsSupplementBackButton = true
self.navigationController?.navigationBar.tintColor = UIColor.universalColorYellow
//title = detailSoccer.detailTitleS
let nib: UINib = UINib(nibName: "CustomSoccerHeaderView", bundle: nil)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "CustomSoccerHeaderView")
tableView.tableFooterView = UIView(frame: .zero)
tableView.estimatedRowHeight = 280
headerView?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 350)
tableView.tableHeaderView = headerView
tableView.tableHeaderView = nil
tableView.contentInset = UIEdgeInsets(top: 350, left: 0, bottom: 0, right: 0)
tableView.addSubview(headerView!)
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName:"FootTableViewCell",bundle:nil), forCellReuseIdentifier: "cellSoc")
self.view.addSubview(tableView)
tableView.rowHeight = UITableView.automaticDimension
tableView.reloadData()
loadMatchSoccer()
tableView.rowHeight = UITableView.automaticDimension
}
That's going to stretch the effect:
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
self.tableView.reloadData()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if headerView != nil {
let yPos: CGFloat = -scrollView.contentOffset.y
if yPos > 0 {
var imgRect: CGRect? = headerView?.frame
imgRect?.origin.y = scrollView.contentOffset.y
imgRect?.size.height = 350 + yPos - 350
headerView?.frame = imgRect!
self.tableView.sectionHeaderHeight = (imgRect?.size.height)!
}
}
}
This is the code for the header section of my file xib class.I think I make a mistake in inheriting the header or loading addsubview.Maybe someone more experienced can tweak my code.I would appreciate that.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomSoccerHeaderView") as! CustomSoccerHeaderView
header.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 350)
header.clickBravisso = detailSoccer
header.nameHeader.text = detailSoccer.matchS
header.countBravo.text = ""
// header.bravoBtn.addTarget(CustomSoccerHeaderView(), action: #selector(CustomSoccerHeaderView.likeBtn(_:)), for: UIControlEvents.touchUpInside)
detailSoccer.imagePrS.getDataInBackground { (data, error) in
header.imageHeader.image = error == nil ? UIImage(data: data!) : nil
}
return header
}
I am using the viewForHeader delegate for UITableView to set the custom header view but i am not setting it with constraints . So now my application requires to support the RTL languages too. My code is given as below:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: menuTableView.frame.size.width, height: 40))
let headerImage = UIImageView()
headerImage.frame = CGRect(x: 15, y: 15, width: 12, height: 12)
let headerLabel = UILabel()
headerLabel.frame = CGRect(x: headerImage.frame.origin.x + headerImage.frame.size.width + 20, y: 10, width:menuTableView.frame.size.width - 40, height: 20)
headerLabel.font = UIFont().robotoLight(withFontSize: 9)
headerLabel.textColor = CustomColor.menuGrey.color
let arrowImage = UIImageView()
arrowImage.isHidden = true
switch section {
case 1:
headerImage.image = UIImage(named: "home_icon")
headerLabel.text = "Home"
case 2:
headerImage.image = UIImage(named: "my_Id_icon")
headerLabel.text = "My ID"
case 3:
headerImage.image = UIImage(named: "my_interest_icon")
headerLabel.text = "My Account"
arrowImage.isHidden = false
if isFirstSectionHidden {
arrowImage.frame = CGRect(x: menuTableView.frame.size.width - 30, y: 15, width: 3, height: 5)
arrowImage.image = UIImage(named: "side_arrow")
} else {
arrowImage.frame = CGRect(x: menuTableView.frame.size.width - 30, y: 15, width: 5, height: 3)
arrowImage.image = UIImage(named: "down_arrow")
}
case 4:
headerImage.image = UIImage(named: "my_profile_icon")
headerLabel.text = "My Profile"
case 5:
headerImage.image = UIImage(named: "language_icon")
headerLabel.text = language
arrowImage.isHidden = false
if isSecondSectionHidden {
arrowImage.frame = CGRect(x: menuTableView.frame.size.width - 30, y: 15, width: 3, height: 5)
arrowImage.image = UIImage(named: "side_arrow")
} else {
arrowImage.frame = CGRect(x: menuTableView.frame.size.width - 30, y: 15, width: 5, height: 3)
arrowImage.image = UIImage(named: "down_arrow")
}
case 6:
headerImage.image = UIImage(named: "notification_icon")
headerLabel.text = "Notifications"
case 7:
headerImage.image = UIImage(named: "about_us_icon")
headerLabel.text = "About Us"
case 8:
headerImage.image = UIImage(named: "faq_icon")
headerLabel.text = "FAQ"
case 9:
headerImage.image = UIImage(named: "contact_us_icon")
headerLabel.text = "Contact Us"
case 10:
headerImage.image = UIImage(named: "logout_icon")
headerLabel.text = "Logout"
default:
print("Something else")
}
let headerClick = UIButton(type: .custom)
headerClick.addTarget(self, action: #selector(self.headerAction), for: .touchUpInside)
headerClick.frame = CGRect(x: 0, y: 0, width: menuTableView.frame.size.width, height: 40)
headerClick.tag = section
headerClick.backgroundColor = UIColor.clear
headerView.addSubview(arrowImage)
headerView.addSubview(headerLabel)
headerView.addSubview(headerImage)
headerView.addSubview(headerClick)
return headerView
}
How can i set the frames and constraints of the headerView , headerImage and headerLabel programatically to support both LTR and RTL?
This is the sample image of the UITableView:
I'm not totally sure if I understood your layout correctly but it should work like this:
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = .lightGray
let headerImage = UIImageView()
headerImage.translatesAutoresizingMaskIntoConstraints = false
headerImage.backgroundColor = .red
headerImage.widthAnchor.constraint(equalToConstant: 12).isActive = true
headerImage.heightAnchor.constraint(equalToConstant: 12).isActive = true
let headerLabel = UILabel()
headerLabel.translatesAutoresizingMaskIntoConstraints = false
headerLabel.backgroundColor = .blue
let arrowImage = UIImageView()
arrowImage.translatesAutoresizingMaskIntoConstraints = false
arrowImage.backgroundColor = .green
arrowImage.widthAnchor.constraint(equalToConstant: 5).isActive = true
arrowImage.heightAnchor.constraint(equalToConstant: 5).isActive = true
let headerClick = UIButton()
headerClick.translatesAutoresizingMaskIntoConstraints = false
headerClick.backgroundColor = .clear
headerClick.isOpaque = false
headerView.addSubview(headerImage)
headerView.addSubview(headerLabel)
headerView.addSubview(arrowImage)
headerView.addSubview(headerClick)
var constraints = [NSLayoutConstraint]()
constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "|-15-[hi]-[hl]-[ai]-15-|", options: [.alignAllCenterY], metrics: nil, views: ["hi": headerImage, "hl": headerLabel, "ai": arrowImage]))
constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[hl]-10-|", options: [], metrics: nil, views: ["hl": headerLabel]))
NSLayoutConstraint.activate(constraints)
headerClick.widthAnchor.constraint(equalTo: headerView.widthAnchor).isActive = true
headerClick.heightAnchor.constraint(equalTo: headerView.heightAnchor).isActive = true
return headerView
}
I only set up the generic layout. You still have to add your custom logic. This is what it looks like:
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.
I have a custom UICollectionViewCell class where I want to add subviews.
My cell class: The SetUpView() method will add all the subvies I need.
import Foundation
import UIKit
class RecipeCell: UICollectionViewCell {
var RecipeImg: UIImage!
var StarRatingImg: UIImage!
var RecipeTitleText = ""
var RecipeTextDescription = ""
var View: UIView!
var ImageContainer: UIImageView!
var FavIcon: UIImageView!
var StarRatingContainer: UIImageView!
var KCAL: UILabel!
var RecipeTitle: UITextView!
var RecipeText: UITextView!
func SetUpView()
{
//DropDown!.backgroundColor = UIColor.blueColor()
self.translatesAutoresizingMaskIntoConstraints = false
//View for recipe
View = UIView(frame: CGRectMake(0, 0, self.frame.width, self.frame.height))
View.backgroundColor = UIColor.whiteColor()
//Recipe image
ImageContainer = UIImageView(frame: CGRectMake(0, 0, View.frame.width, View.frame.height/2))
ImageContainer.image = RecipeImg
ImageContainer.contentMode = .ScaleToFill
//Recipe favorit icon
FavIcon = UIImageView(frame: CGRectMake(ImageContainer.frame.width - 35, 5, 30, 30))
FavIcon.image = UIImage(named: "LikeHeart")
//Star rating image
StarRatingContainer = UIImageView(frame: CGRectMake(10, ImageContainer.frame.height + 5, ImageContainer.frame.width - 20, (View.frame.height/2) * (1/5)))
StarRatingContainer.image = StarRatingImg
StarRatingContainer.contentMode = .ScaleAspectFit
//RecipeTitle container
RecipeTitle = UITextView(frame: CGRectMake(10, StarRatingContainer.frame.height + ImageContainer.frame.height + 10, View.frame.width - 20, 30))
RecipeTitle.font = UIFont(name: "OpenSans-Semibold", size: 12)
//RecipeTitle.backgroundColor = UIColor.redColor()
RecipeTitle.editable = false
RecipeTitle.text = RecipeTitleText
RecipeTitle.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
//RecipeText container
RecipeText = UITextView(frame: CGRectMake(10, StarRatingContainer.frame.height + ImageContainer.frame.height + RecipeTitle.frame.height + 15, View.frame.width - 20, 50))
RecipeText.font = UIFont(name: "OpenSans", size: 12)
//RecipeText.backgroundColor = UIColor.grayColor()
RecipeText.editable = false
RecipeText.text = RecipeTextDescription
RecipeText.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
//KCAL label
KCAL = UILabel(frame: CGRectMake(15, StarRatingContainer.frame.height + ImageContainer.frame.height + RecipeTitle.frame.height + RecipeText.frame.height + 20, 200, 20))
KCAL.text = "420 KCAL. PER. PORTION"
KCAL.font = UIFont(name: "OpenSans-Bold", size: 10)
KCAL.textColor = UIColor(CGColor: "#dc994a".CGColor)
//Adding the views
self.addSubview(View)
View.addSubview(ImageContainer)
View.addSubview(KCAL)
View.addSubview(StarRatingContainer)
View.addSubview(RecipeTitle)
View.addSubview(RecipeText)
ImageContainer.addSubview(FavIcon)
View.bringSubviewToFront(ImageContainer)
}
}
I have a UICollectionView which uses the custom cell class.
I create my UICollectionView in viewDidLoad()
// Create Collection view
layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: screenWidth/MenuViewConst - 1, height: screenWidth - 1)
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
collectionView = UICollectionView(frame: CGRect(x: 0, y: 105, width: self.view.frame.width, height: self.view.frame.height - 150), collectionViewLayout: layout)
collectionView?.tag = 5
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(RecipeCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
collectionView!.backgroundColor = UIColor.lightGrayColor()
collectionView!.contentInset.top = 0
In cellForItemAtIndexPath delegate I set up the UICollectionView to use my custom cell class. But I can't call the SetUpView() method from my custom cell class here, because that will just keep adding subviews on subviews. I can't figure out how to add the subviews to the UICollectionViewCell before entering the delegate. Hope you guys can help - Thank you
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! RecipeCell
let recipe = self.RecipeArr[indexPath.row]
cell.backgroundColor = UIColor.grayColor()
cell.layer.borderColor = UIColor.whiteColor().CGColor
cell.layer.borderWidth = 0.5
cell.RecipeImg = UIImage(named: "Burger")
cell.StarRatingImg = UIImage(named: "StarRating")
cell.RecipeTitleText = recipe["name"].string!
cell.RecipeTextDescription = recipe["instruction"].string!
//BAD IDEA!
//cell.SetUpView()
print("new cell")
return cell
}
You need to use init(frame: CGRect) inherited function in the UICollectionViewCell .
class RecipeCell: UICollectionViewCell {
var imageView : UIImageView?
override init(frame: CGRect) {
super.init(frame: frame)
//initialize all your subviews.
imageView = UIImageView()
}
}
also don't forget to register your custom class in the viewDidLoad function
collectionView!.registerClass(RecipeCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
and your collectionview delegate would be like this
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! RecipeCell
cell.imageView.image = UIImage(named:"yourImage.png")
}