On Change Function In Class Swift - swift

I created a DropDownMenu class, but I need to know when the value changed in the ViewController. Like a UIScrollView didScroll, but for the value change in my class.
func scrollViewDidScroll(_ scrollView: UIScrollView)
I need something like that, but for the class!
Here is the class...
class DropDownMenu: UIStackView {
var options: [String]! = [] // Labels for all of the options
var titleButton: UIButton! = UIButton() // The Main Title Button
var target: UIViewController! // The target to get the main view. Maybe remove and automatically do it later
var textColor: UIColor! = UIColor.black // The Color of the text of the options
var bgColor: UIColor! = UIColor.clear
var borderWidth: CGFloat! = 0.0
var borderColor: CGColor! = UIColor.black.cgColor
var uiBorderColor: UIColor? = nil
var cornerRadius: CGFloat! = 0.0
var font: UIFont! = UIFont.systemFont(ofSize: 18)
var images: [UIImage]? = nil
var imageInsets: [UIEdgeInsets]? = nil
var imageInset: UIEdgeInsets? = nil
var value: String! {
get {
return currentSelected
}
set {
currentSelected = newValue
}
}
private var currentSelected: String! = ""
init(options: [String]) {
self.options = options
super.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.axis = .vertical
}
init(titleButton: UIButton) {
self.titleButton = titleButton
super.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.axis = .vertical
}
func createDropDownMenu() {
currentSelected = titleButton.titleLabel?.text
let mainFrame = titleButton.frame
print("Frame: \(mainFrame)")
print("StackView frame: \(self.frame), axis: \(self.axis)")
if uiBorderColor != nil {
borderColor = uiBorderColor!.cgColor
}
self.widthAnchor.constraint(equalToConstant: mainFrame.width).isActive = true
self.leftAnchor.constraint(equalTo: titleButton.leftAnchor).isActive = true
self.topAnchor.constraint(equalTo: titleButton.bottomAnchor, constant: self.spacing).isActive = true
var y: CGFloat = 0
var place = 0
for title in self.options {
let button = UIButton(frame: CGRect(x: 0, y: y, width: mainFrame.width, height: mainFrame.height))
button.setTitle(title, for: .normal)
button.setTitleColor(textColor, for: .normal)
button.backgroundColor = bgColor
button.addTarget(self, action: #selector(dropDownOptionClicked(_:)), for: .touchUpInside)
button.layer.cornerRadius = cornerRadius
button.layer.borderWidth = borderWidth
button.layer.borderColor = borderColor
button.titleLabel?.font = font
if images != nil {
button.setBackgroundImage(images![place], for: .normal)
if imageInsets != nil {
button.imageEdgeInsets = imageInsets![place]
} else if imageInsets == nil && imageInset != nil{
button.imageEdgeInsets = imageInset!
} else {
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
print("Button: \(button), Title: \(String(describing: button.titleLabel?.text)), Target: \(button.allTargets)")
button.isHidden = true
button.alpha = 0
self.addArrangedSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: mainFrame.width).isActive = true
button.heightAnchor.constraint(equalToConstant: mainFrame.height).isActive = true
print("Subviews: \(self.arrangedSubviews)")
y += mainFrame.height
place += 1
print("y: \(y)")
}
}
#objc func openDropDown(_ sender: UIButton) {
print("Open DropDownMenu")
self.arrangedSubviews.forEach { (button) in
UIView.animate(withDuration: 0.7) {
button.isHidden = !button.isHidden
button.alpha = button.alpha == 0 ? 1 : 0
self.target.view.layoutIfNeeded()
}
}
}
#objc private func dropDownOptionClicked(_ sender: UIButton) {
let text = sender.titleLabel?.text
print(text as Any)
currentSelected = text
print("Value: \(String(describing: value))")
titleButton.setTitle(text, for: .normal)
openDropDown(sender)
}
init() {
super.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.axis = .vertical
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is the creation in ViewController...
let titleButton = UIButton(frame: CGRect(x: 50, y: 290, width: 100, height: 40))
titleButton.backgroundColor = UIColor.white
titleButton.setTitle("Grade", for: .normal)
titleButton.setTitleColor(UIColor.black, for: .normal)
titleButton.layer.borderWidth = 2
titleButton.layer.cornerRadius = 10
let dp = DropDownMenu(options: ["1", "Freshman", "Sophomore", "Junior", "Senior", "College"])
dp.titleButton = titleButton
dp.target = self
dp.borderWidth = 2
dp.spacing = 5
dp.cornerRadius = 10
dp.bgColor = UIColor.white
titleButton.addTarget(dp, action: #selector(dp.openDropDown(_:)), for: .touchUpInside)
infoBox.addSubview(titleButton)
infoBox.addSubview(dp)
dp.createDropDownMenu()
The class works as expected.
I really need help on this. No answer is a bad one. This is all of my code.

You usually do that with a delegate:
class DropDownMenu: UIStackView {
weak var delegate: DropDownMenuDelegate?
var value: String! {
get {
return currentSelected
}
set {
if currentSelected != newValue {
currentSelected = newValue
self.delegate?.valueDidChange(self)
}
}
}
}
protocol DropDownMenuDelegate: class {
func valueDidChange(_ menu: DropDownMenu)
}
Then in your View Controller:
let dp = DropDownMenu(options: ["1", "Freshman", "Sophomore", "Junior", "Senior", "College"])
dp.delegate = self
(typing this in the blind as I'm away from Xcode so there may be syntax errors)

Related

Custom Segment Control with UIView, only the first button works

I realized a Custom Segment Control with UIView, only the first button works.
When I click on the other buttons, they don't change color, and the first button stays with the active background color.
I would like to give the buttons an on / off effect, but only the first button remains on.
I believe the problem is inside the method:
func didSelectButton (at index: Int) {
but I can't find what's missing
import UIKit
#IBDesignable class CustomSegmentedView: UIView {
#IBInspectable var selectedBackgroundColor = Colors.colorViolet1 {
didSet {
self.slideView.backgroundColor = selectedBackgroundColor
}
}
#IBInspectable var selectedTextColor: UIColor = UIColor.white
#IBInspectable var buttonText1: String = ConstantFile.buttonMonitoring {
didSet {
self.buttonTitles[0] = buttonText1
}
}
#IBInspectable var buttonText2: String = ConstantFile.buttonAlert {
didSet {
self.buttonTitles[1] = buttonText2
}
}
#IBInspectable var buttonText3: String = ConstantFile.buttonActivities {
didSet {
self.buttonTitles[2] = buttonText3
}
}
#IBInspectable var buttonText4: String = ConstantFile.buttonSettings {
didSet {
self.buttonTitles[3] = buttonText4
}
}
#IBInspectable var image1: UIImage = UIImage() {
didSet {
self.buttonImages[0] = image1
}
}
#IBInspectable var image2: UIImage = UIImage() {
didSet {
self.buttonImages[1] = image2
}
}
#IBInspectable var image3: UIImage = UIImage() {
didSet {
self.buttonImages[2] = image3
}
}
#IBInspectable var image4: UIImage = UIImage() {
didSet {
self.buttonImages[3] = image4
}
}
#IBInspectable var image1Selected: UIImage = UIImage() {
didSet {
self.buttonSelectImages[0] = image1Selected
}
}
#IBInspectable var image2Selected: UIImage = UIImage() {
didSet {
self.buttonSelectImages[1] = image2Selected
}
}
#IBInspectable var image3Selected: UIImage = UIImage() {
didSet {
self.buttonSelectImages[2] = image3Selected
}
}
#IBInspectable var image4Selected: UIImage = UIImage() {
didSet {
self.buttonSelectImages[3] = image4Selected
}
}
#IBInspectable var startingIndex: Int = 0 {
didSet {
if startingIndex > 3 {
startingIndex = 3
self.didSelectButton(at: startingIndex)
} else if startingIndex < 0 {
startingIndex = 0
self.didSelectButton(at: startingIndex)
} else {
self.didSelectButton(at: startingIndex)
}
}
}
lazy var button1: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("", for: .normal)
button.titleLabel?.font = UIFont.fontHelveticaBold14
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0)
return button
}()
lazy var button2: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("", for: .normal)
button.titleLabel?.font = UIFont.fontHelveticaBold14
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0)
return button
}()
lazy var button3: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("", for: .normal)
button.titleLabel?.font = UIFont.fontHelveticaBold14
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0)
return button
}()
lazy var button4: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("", for: .normal)
button.titleLabel?.font = UIFont.fontHelveticaBold14
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0)
return button
}()
lazy var stackView: UIStackView = UIStackView(arrangedSubviews: [])
lazy var slideView: UIView = {
var view = UIView(frame: CGRect.zero)
view.backgroundColor = self.selectedBackgroundColor
return view
}()
private var currentIndex: Int = 0
private var buttons: [UIButton] = []
private lazy var buttonTitles:[String] = [buttonText1, buttonText2, buttonText3, buttonText4]
private lazy var buttonImages:[UIImage] = [image1, image2, image3, image4]
private lazy var buttonSelectImages:[UIImage] = [image1Selected, image2Selected, image3Selected, image4Selected]
var delegate: CustomSegmentedViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
self.buttons = [button1, button2, button3, button4]
self.buttonTitles = [buttonText1, buttonText2, buttonText3, buttonText4]
self.buttonImages = [image1, image2, image3, image4]
self.buttonSelectImages = [image1Selected, image2Selected, image3Selected, image4Selected]
button1.sizeToFit()
button2.sizeToFit()
button3.sizeToFit()
button4.sizeToFit()
stackView.addArrangedSubview(button1)
stackView.addArrangedSubview(button2)
stackView.addArrangedSubview(button3)
stackView.addArrangedSubview(button4)
stackView.axis = .horizontal
stackView.distribution = .fillProportionally
stackView.alignment = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(slideView)
self.addSubview(stackView)
stackView.pinEdges(to: self)
button1.addTarget(self, action: #selector(CustomSegmentedView.buttonTapped(sender:)), for: .touchUpInside)
button2.addTarget(self, action: #selector(CustomSegmentedView.buttonTapped(sender:)), for: .touchUpInside)
button3.addTarget(self, action: #selector(CustomSegmentedView.buttonTapped(sender:)), for: .touchUpInside)
button4.addTarget(self, action: #selector(CustomSegmentedView.buttonTapped(sender:)), for: .touchUpInside)
button1.setTitle("", for: .normal)
button2.setTitle("", for: .normal)
button3.setTitle("", for: .normal)
button4.setTitle("", for: .normal)
}
override func layoutSubviews() {
super.layoutSubviews()
button1.setImage(buttonImages[0], for: .normal)
button2.setImage(buttonImages[1], for: .normal)
button3.setImage(buttonImages[2], for: .normal)
button4.setImage(buttonImages[3], for: .normal)
self.setupFirstSelection()
}
#objc func buttonTapped(sender :UIButton!) {
switch sender {
case button1:
didSelectButton(at: 0)
break
case button2:
didSelectButton(at: 1)
break
case button3:
didSelectButton(at: 2)
break
case button4:
didSelectButton(at: 3)
break
default:
break
}
}
func didSelectButton(at index: Int) {
self.delegate?.didSelectPage(index: index)
let oldButton = self.buttons[self.currentIndex]
let newButton = self.buttons[index]
newButton.alpha = 0.0
oldButton.setImage(self.buttonImages[self.currentIndex], for: .normal)
newButton.setImage(self.buttonSelectImages[index], for: .normal)
UIView.animate(withDuration: 0.1) {
oldButton.setTitle("", for: .normal)
newButton.setTitle(self.buttonTitles[index], for: .normal)
self.stackView.layoutSubviews()
self.layoutIfNeeded()
newButton.alpha = 1.0
}
UIView.animate(withDuration: 0.2, delay: 0, options: [], animations: {
self.slideView.frame = newButton.frame
self.layoutIfNeeded()
}, completion: nil)
self.currentIndex = index
}
func setupFirstSelection() {
let index = self.startingIndex
let newButton = self.buttons[index]
newButton.setTitle(self.buttonTitles[index], for: .normal)
newButton.setImage(self.buttonSelectImages[index], for: .normal)
self.stackView.layoutSubviews()
self.slideView.frame = newButton.frame
self.slideView.layer.cornerRadius = self.slideView.frame.height/2.0
self.currentIndex = index
}
}
extension UIView {
func pinEdges(to other: UIView) {
leadingAnchor.constraint(equalTo: other.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: other.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: other.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: other.bottomAnchor).isActive = true
}
}
protocol CustomSegmentedViewDelegate {
func didSelectPage(index: Int)
}
issue is in your layoutSubviews() you call every time when you call layoutSubviews() this function automatically set first self.setupFirstSelection()
replace layoutSubviews() by commenting self.setupFirstSelection()
override func layoutSubviews() {
super.layoutSubviews()
button1.setImage(buttonImages[0], for: .normal)
button2.setImage(buttonImages[1], for: .normal)
button3.setImage(buttonImages[2], for: .normal)
button4.setImage(buttonImages[3], for: .normal)
//self.setupFirstSelection()
}

What is the reason of an index to be out of range and how to fix this issue in my code?

This is audio player, It worked fine before I started to try stream audios from firebase. Now data is downloaded from the firebase database, the image, title and subtitle are being displayed correctly, but when user taps on the cell the audio doesn't play and it throws a fatal error, Thread 1: Fatal error: Index out of range
This is a table view controller which lists the audio tracks from firebase database.
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 cancellable: AnyCancellable?
private var viewModel = AudiosViewModel()
var paragraphs = [Audio]()
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.fetchData()
self.title = "Audio Lessons"
table.delegate = self
table.dataSource = self
cancellable = viewModel.$audios.sink { _ in
DispatchQueue.main.async{
self.table.reloadData()
}
} // Do any additional setup after loading the view.
}
// Table
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: "Cell", for: indexPath)
let audio = viewModel.audios[indexPath.row]
tableView.tableFooterView = UIView()
cell.textLabel?.text = audio.albumName
cell.detailTextLabel?.text = audio.name
cell.accessoryType = .disclosureIndicator
let imageURL = audio.audioImageName
cell.imageView?.sd_imageIndicator = SDWebImageActivityIndicator.gray
cell.imageView?.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))")
}
})
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
let backgroundView = UIView()
backgroundView.backgroundColor = UIColor(named: "AudioLessonsCellHighlighted")
cell.selectedBackgroundView = backgroundView
cell.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// present the player
let position = indexPath.row
//lessons
guard let vc = storyboard?.instantiateViewController(identifier: "AudioPlayer") as? AudioPlayerViewController else {
return
}
vc.paragraphs = paragraphs
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
}
}
}
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
class AudioPlayerViewController: UIViewController {
public var position: Int = 0
public var paragraphs: [Audio] = []
#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.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 = true
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 = UIImage(named: song.audioImageName)
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)
})
}
}
private func setupView() {
setupConstraints()
}
}
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
}
}
AudioViewModel
import Foundation
import FirebaseFirestore
class AudiosViewModel: ObservableObject {
#Published var audios = [Audio]()
private var db = Firestore.firestore()
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 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 ?? ""
print(data)
return Audio(name: name, albumName: albumName, paragraphNumber: paragraphNumber, audioImageName: audioImageName, trackURL: trackURL)
}
}
}
}
Audio Structure
import Foundation
struct Audio {
let name: String
let albumName: String
let paragraphNumber: String
let audioImageName: String
let trackURL: String
}
Assign/append value to your paragraphs and also check your position if there will be no values/wrong position it'll throw index out of range

Button action in CollectionView cell swift

I have a button in my CollectionViewCell and I added an action to the button but the action does not get triggered. I am currently using the cell independently.
class BalanceCell: UICollectionViewCell {
lazy var toggleBtn: ToggleButton = {
let view = ToggleButton()
view.isUserInteractionEnabled = true
view.addTarget(self, action: #selector(onTapped), for: .touchUpInside)
return view
}()
lazy var titleLabel: LabelX = {
let view = LabelX()
view.text = "My Available Balance"
view.font = .systemFont(ofSize: 16, weight: .regular)
return view
}()
lazy var amountLabel: TextFieldX = {
let view = TextFieldX()
view.isSecureTextEntry = true
view.text = "N000,000.00"
view.textAlignment = .center
view.font = .systemFont(ofSize: 26, weight: .medium)
return view
}()
private lazy var stackView: StackViewX = {
let view = StackViewX(arrangedSubviews: [titleLabel, amountLabel])
view.alignment = .center
view.axis = .vertical
view.distribution = .fillProportionally
view.spacing = 4
return view
}()
lazy var bgImageView: ImageViewX = {
let view = ImageViewX()
view.backgroundColor = R.color.roundEdgeButtonBgColor()
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func layoutSubviews() {
super.layoutSubviews()
setupViewHierarchy()
setupConstraints()
setupProperties()
translatesAutoresizingMaskIntoConstraints = false
}
func setupViewHierarchy() {
addSubview(bgImageView)
bgImageView.addSubviews([stackView, toggleBtn])
}
func setupConstraints() {
bgImageView.fillToSuperview()
stackView.centerInSuperview()
toggleBtn.anchor(verticalY: bgImageView.centerYAnchor, horizontalX: nil, paddingX: 0, paddingY: 0, width: 24, height: 24, enableInsets: false)
toggleBtn.rightAnchor.constraint(equalTo: stackView.leftAnchor, constant: -18).isActive = true
}
func setupProperties() {
bgImageView.backgroundColor = .red
bgImageView.layer.cornerRadius = 15
layer.cornerRadius = 15
}
func setup(with title: String, amount: String, bgImageViewColor: UIColor) {
titleLabel.text = title
amountLabel.text = amount
bgImageView.backgroundColor = bgImageViewColor
}
#objc private func onTapped() {
self.setSecureMode(self.toggleBtn.isSecure)
}
private func setSecureMode(_ secure: Bool) {
amountLabel.isSecureTextEntry = secure
let tempText = amountLabel.text
amountLabel.text = tempText
}
}
This is the way I use it.
lazy var balacnceView: BalanceCell = {
let view = BalanceCell()
return view
}()
ANy help as to why the Togelbutton is not getting called is appritiatedswift
The default value of isUserInteractionEnabled property is true in UIButton. You do not need to set this property to true.
However, the default value of isUserInteractionEnabled property is false in UIImageView. If you want to enable action in UIImageView (or in subviews of UIImageView), then set isUserInteractionEnabled to true.
In your code, you have put all subviews inside bgImageView. Just add this line of code:
bgImageView.isUserInteractionEnabled = true

swift 3.0 UISlider left thumbs

This's code,
slider = UISlider(frame: CGRect(x: 80,y: 9.5,width: UIScreen.main.bounds.width - 160,height: 15))
slider.minimumTrackTintColor = UIColor.orange
slider.isContinuous = false
slider.addTarget(self, action: #selector(pageChange), for: .valueChanged)
slider.setThumbImage(UIImage(named:"RM_3"), for: .normal)
bottomBar?.addSubview(slider)
How to clean up the left thumbs???
IN Swift 3.0
import UIKit
class DBSlider: UISlider {
#IBInspectable var thumbImage: UIImage?
#IBInspectable var minTrackColor:UIColor?
#IBInspectable var maxTrackColor:UIColor?
// MARK: Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
if let thumbImage = thumbImage
{0
self.setThumbImage(thumbImage, for: .normal)
}
if let minTrackColor = minTrackColor
{
self.minimumTrackTintColor = minTrackColor
}
if let maxTrackColor = maxTrackColor
{
self.maximumTrackTintColor = maxTrackColor
}
}
}

How to change color of pair of buttons tapped?

I have 3 pairs of buttons named (one,numOne),(two,numTwo),(three,numThree). Initially tintColor of all the buttons is black. I like to change color of pair of buttons to blue if one button in those pair is tapped and back to black when other pair is tapped. I am able to do it by following code but is there any other shortest way than below?
#IBOutlet weak var one: UIButton!
#IBOutlet weak var two: UIButton!
#IBOutlet weak var three: UIButton!
#IBOutlet weak var numOne: UIButton!
#IBOutlet weak var numTwo: UIButton!
#IBOutlet weak var numThree: UIButton!
#IBAction func buttonTapped(_ sender: UIButton) {
if sender == one || sender == numOne
{
one.tintColor = UIColor.blue
numOne.tintColor = UIColor.blue
two.tintColor = UIColor.black
numTwo.tintColor = UIColor.black
three.tintColor = UIColor.black
numThree.tintColor = UIColor.black
}
else if sender == two || sender == numTwo
{
two.tintColor = UIColor.blue
numTwo.tintColor = UIColor.blue
one.tintColor = UIColor.black
numOne.tintColor = UIColor.black
three.tintColor = UIColor.black
numThree.tintColor = UIColor.black
}
else
{
three.tintColor = UIColor.blue
numThree.tintColor = UIColor.blue
two.tintColor = UIColor.black
numTwo.tintColor = UIColor.black
one.tintColor = UIColor.black
numOne.tintColor = UIColor.black
}
}
var allButtons: [[UIButton]] = [[one, numOne], [two, numTwo], [three, numThree]]
func tap(_ sender: UIButton) {
allButtons.forEach { buttons in
if buttons.contains(sender) {
buttons.forEach{ $0.tintColor = .blue }
} else {
buttons.forEach{ $0.tintColor = .black }
}
}
}
shorter one XD
buttons.contains(sender) ? buttons.forEach{ $0.tintColor = .blue } : buttons.forEach{ $0.tintColor = .black }
There are multiple ways to do that like using tag, array of array of UIButtons, switch case, using stack view, etc.
Here an example using tag and outlet collection.
First create outlet collection of outlets.
#IBOutlet var btn1: [UIButton]!
#IBOutlet var btn2: [UIButton]!
#IBOutlet var btn3: [UIButton]!
then
#IBAction func buttonClicked(_ sender: UIButton) {
if sender.tag == 1 {
btnClicked(clr1: UIColor.black, clr2: UIColor.blue, clr3: UIColor.blue)
}
if sender.tag == 2 {
btnClicked(clr1: UIColor.blue, clr2: UIColor.black, clr3: UIColor.blue)
}
if sender.tag == 3 {
btnClicked(clr1: UIColor.blue, clr2: UIColor.blue, clr3: UIColor.black)
}
}
func btnClicked(clr1: UIColor, clr2: UIColor, clr3: UIColor) {
for i in 0..<2 {
btn1[i].tintColor = clr1
btn2[i].tintColor = clr2
btn3[i].tintColor = clr3
}
}
To short your code, first you need to declare two more UIButton instance with your Button outlet, now use these two buttons in your Button action.
var firstSelected = UIButton()
var secondSelected = UIButton()
#IBAction func buttonTapped(_ sender: UIButton) {
self.firstSelected.tintColor = .black
self.secondSelected.tintColor = .black
if sender == one || sender == numOne {
self.firstSelected = one
self.secondSelected = numOne
}
else if sender == two || sender == numTwo {
self.firstSelected = two
self.secondSelected = numTwo
}
else {
self.firstSelected = three
self.secondSelected = numThree
}
self.firstSelected.tintColor = .blue
self.secondSelected.tintColor = .blue
}
This can be a shortest format:
#IBAction func buttonTapped(_ sender: UIButton) {
one.tintColor = .black
numOne.tintColor = .black
two.tintColor = .black
numTwo.tintColor = .black
three.tintColor = .black
numThree.tintColor = .black
if sender == one || sender == numOne {
one.tintColor = .blue
numOne.tintColor = .blue
} else if sender == two || sender == numTwo {
two.tintColor = .blue
numTwo.tintColor = .blue
} else {
three.tintColor = .blue
numThree.tintColor = .blue
}
}
Or use Switch
switch sender {
case one, numOne:
one.tintColor = .blue
numOne.tintColor = .blue
case two, numTwo:
two.tintColor = .blue
numTwo.tintColor = .blue
default:
three.tintColor = .blue
numThree.tintColor = .blue
}
Or using $0 to make it shorter for the accepted answer:
allButtons.forEach {
let color: UIColor = $0.contains(sender) ? .blue : .black
$0.forEach { $0.tintColor = color }
}