J'ai commencé à créer mon premier projet Open Source que j'aimerais partager avec le monde entier. C'est une simple vue Audio Player qui fonctionnera avec le protocole AudioProvidable, puis jouera une donnée audio après sa réception.iOS Contrôle personnalisé avec éléments dynamiques
Pour l'instant, j'ai une classe IBDesignable hérite de UIView qui se construit en définissant des contraintes pour ses sous-vues comme celle-ci:
override init(frame: CGRect) {
super.init(frame: frame)
#if !TARGET_INTERFACE
translatesAutoresizingMaskIntoConstraints = false
#endif
prepareView()
updateUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepareView()
updateUI()
}
func prepareView() {
//Necessary in order to set our own constraints
//btnPlay is already doing the same inside buttonWithImage(_:)
spinner.translatesAutoresizingMaskIntoConstraints = false
sliderAudioProgress.translatesAutoresizingMaskIntoConstraints = false
//Add necessary subviews to start setting up layout
addSubview(sliderAudioProgress)
addSubview(lblAudioDuration)
addSubview(lblAudioProgress)
addSubview(spinner)
addSubview(btnPlay)
addSubview(lblTrackNumber)
addSubview(btnNextTrack)
addSubview(btnPreviousTrack)
//Height of play & next/previous track buttons defines AudioPlayerView height
let viewHeight:CGFloat = 48 * 2
//Setup UI layout using constraints
NSLayoutConstraint.activate([
heightAnchor.constraint(equalToConstant: viewHeight),
//Play button constraints
btnPlay.topAnchor.constraint(equalTo: topAnchor),
btnPlay.leadingAnchor.constraint(equalTo: leadingAnchor),
//Spinner constraints
spinner.centerYAnchor.constraint(equalTo: btnPlay.centerYAnchor),
spinner.centerXAnchor.constraint(equalTo: btnPlay.centerXAnchor),
//Progress label constraints
lblAudioProgress.leadingAnchor.constraint(equalTo: btnPlay.trailingAnchor),
lblAudioProgress.centerYAnchor.constraint(equalTo: btnPlay.centerYAnchor),
//Duration label constraints
lblAudioDuration.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -spacing),
lblAudioDuration.centerYAnchor.constraint(equalTo: btnPlay.centerYAnchor),
//Audio Progress Slider constraints
sliderAudioProgress.leadingAnchor.constraint(equalTo: lblAudioProgress.trailingAnchor, constant: spacing),
sliderAudioProgress.trailingAnchor.constraint(equalTo: lblAudioDuration.leadingAnchor, constant: -spacing),
sliderAudioProgress.centerYAnchor.constraint(equalTo: btnPlay.centerYAnchor),
lblTrackNumber.centerXAnchor.constraint(equalTo: centerXAnchor),
btnPreviousTrack.topAnchor.constraint(equalTo: btnPlay.bottomAnchor),
btnPreviousTrack.trailingAnchor.constraint(equalTo: lblTrackNumber.leadingAnchor, constant: -spacing),
btnNextTrack.topAnchor.constraint(equalTo: btnPlay.bottomAnchor),
btnNextTrack.leadingAnchor.constraint(equalTo: lblTrackNumber.trailingAnchor, constant: spacing),
lblTrackNumber.centerYAnchor.constraint(equalTo: btnNextTrack.centerYAnchor)
])
//Spinner setup:
spinner.hidesWhenStopped = true
//btnPlay setup:
btnPlay.addTarget(self, action: #selector(AudioPlayerView.playButtonPressed), for: .touchUpInside)
//Slider setup
sliderAudioProgress.thumbTintColor = UIColor(keyFromAppColorPalette: "secondary_color")
sliderAudioProgress.addTarget(self, action: #selector(AudioPlayerView.durationSliderValueChanged), for: .valueChanged)
sliderAudioProgress.addTarget(self, action: #selector(AudioPlayerView.durationSliderReleased), for: .touchUpInside)
sliderAudioProgress.isEnabled = false
sliderAudioProgress.isContinuous = true
sliderAudioProgress.semanticContentAttribute = .playback
//View's UI effects to make it look better
layer.cornerRadius = 6
layer.masksToBounds = true
layer.borderColor = UIColor.black.cgColor
layer.borderWidth = 2
semanticContentAttribute = .playback
spinner.startAnimating()
btnPlay.isHidden = true
}
En conséquence qui est ce que nous avons lorsque l'audio reçoit:
Après l'audio reçu:
Dans le cas où il n'y a qu'un seul son pour lire la partie inférieure, les commutations entre les pistes sont redondantes. Où dois-je mettre un code de vérification des pistes audio pour redéfinir mon interface utilisateur? Je ne veux pas appeler prepareView()
dans didSet {}
de tableau de source audio, parce que je ne veux pas que les éléments existants soient ajoutés deux fois. Cordialement.
Je ne suis pas clair sur ce vous demandez. (1) IBDesignable est un outil * design-time *, donc vous ne pouvez pas savoir à ce moment-là combien de pistes il y a. Ce morceau (du moins avec ce que vous avez dit) * doit * faire partie du code * run-time *. (2) En supposant cela, placez les vues de dessous dans une sorte de vue de conteneur (ce peut être un simple UIView) et au moment approprié dans le traitement - probablement une sorte de fermeture après "réception de l'audio" - cacher le tout. (3) Donc, faites en sorte que ce contrôle de conteneur ait une apparence * légèrement * différente dans IB pour indiquer qu'il sera caché s'il y a une piste à jouer. – dfd
@dfd, merci pour votre réponse et une explication claire des choses. Je n'ai pas entièrement compris (3) d clause. Comment ce comportement pourrait être implémenté afin que 'UIView' ait une apparence différente dans IB plutôt que dans _run-time_. Merci. – GeRyCh
Bonne question - Je n'y ai pas réfléchi. :-) Mon image mentale était quelque chose comme un look légèrement "plus léger" dans IB, en le modifiant dans le code, mais après un examen plus approfondi c'est déroutant pour les autres développeurs. Deux autres idées: (1) Considérant qu'il n'y a rien de vraiment * faux * avec l'affichage du bas s'il n'y a qu'une seule piste, pourquoi ne pas exposer une propriété comme * hideTrackCount * dans IB? (2) Au lieu de légèrement modifier l'apparence du fond, brouiller. Il utilise le même style (police, couleur, arrière-plan) que le reste. [De ces deux idées, j'aime mieux le premier.] – dfd