我有一個來自 SwiftySound 框架的名為 Sound 的類和一個名為 SoundWrapper 的子類,這使得它可以散列。現在我在子類中添加了一個字串屬性“imageName”,這樣我就可以為每個聲音添加一個影像。因此,我需要使用 super.init 為超類撰寫初始化程式。
但是,當我嘗試這樣做時,錯誤顯示:“url”由于“私有”保護級別而無法訪問。
有什么辦法可以解決這個問題嗎?
需要明確的是,這是我第一次在 Swift 中創建子類,所以我可能會在這里犯一些明顯的錯誤,并且可能需要完全不同的方法。
這是我的子類:
class SoundWrapper:Sound, Hashable{
let id = UUID()
let imageName : String
init(imageName: String) {
self.imageName = imageName
super.init(url: url)
// Error: 'url' is inaccessible due to 'private' protection level
}
public func hash(into hasher: inout Hasher) {
hasher.combine(0)
}
static func == (lhs: SoundWrapper, rhs: SoundWrapper) -> Bool {
return lhs.id == rhs.id
}
}
我打算如何初始化子類:
let backgroundAudio1 = SoundWrapper(imageName: "image1", url: getFileUrl(name: "backgroundAudio1"))
我如何獲取網址:
func getFileUrl (name: String) -> URL {
let filePath = Bundle.main.path(forResource: name, ofType: "wav")!
return URL(fileURLWithPath: filePath)
}
這是我從中獲取 Sound 類的 SwiftySound 框架:
import Foundation
import AVFoundation
#if os(iOS) || os(tvOS)
/// SoundCategory is a convenient wrapper for AVAudioSessions category constants.
public enum SoundCategory {
/// Equivalent of AVAudioSession.Category.ambient.
case ambient
/// Equivalent of AVAudioSession.Category.soloAmbient.
case soloAmbient
/// Equivalent of AVAudioSession.Category.playback.
case playback
/// Equivalent of AVAudioSession.Category.record.
case record
/// Equivalent of AVAudioSession.Category.playAndRecord.
case playAndRecord
fileprivate var avFoundationCategory: AVAudioSession.Category {
get {
switch self {
case .ambient:
return .ambient
case .soloAmbient:
return .soloAmbient
case .playback:
return .playback
case .record:
return .record
case .playAndRecord:
return .playAndRecord
}
}
}
}
#endif
/// Sound is a class that allows you to easily play sounds in Swift. It uses AVFoundation framework under the hood.
open class Sound {
// MARK: - Global settings
/// Number of AVAudioPlayer instances created for every sound. SwiftySound creates 5 players for every sound to make sure that it will be able to play the same sound more than once. If your app doesn't need this functionality, you can reduce the number of players to 1 and reduce memory usage. You can increase the number if your app plays the sound more than 5 times at the same time.
public static var playersPerSound: Int = 10 {
didSet {
stopAll()
sounds.removeAll()
}
}
#if os(iOS) || os(tvOS)
/// Sound session. The default value is the shared `AVAudioSession` session.
public static var session: Session = AVAudioSession.sharedInstance()
/// Sound category for current session. Using this variable is a convenient way to set AVAudioSessions category. The default value is .ambient.
public static var category: SoundCategory = {
let defaultCategory = SoundCategory.ambient
try? session.setCategory(defaultCategory.avFoundationCategory)
return defaultCategory
}() {
didSet {
try? session.setCategory(category.avFoundationCategory)
}
}
#endif
private static var sounds = [URL: Sound]()
private static let defaultsKey = "com.moonlightapps.SwiftySound.enabled"
/// Globally enable or disable sound. This setting value is stored in UserDefaults and will be loaded on app launch.
public static var enabled: Bool = {
return !UserDefaults.standard.bool(forKey: defaultsKey)
}() { didSet {
let value = !enabled
UserDefaults.standard.set(value, forKey: defaultsKey)
if value {
stopAll()
}
}
}
private let players: [Player]
private var counter = 0
/// The class that is used to create `Player` instances. Defaults to `AVAudioPlayer`.
public static var playerClass: Player.Type = AVAudioPlayer.self
/// The bundle used to load sounds from filenames. The default value of this property is Bunde.main. It can be changed to load sounds from another bundle.
public static var soundsBundle: Bundle = .main
// MARK: - Initialization
/// Create a sound object.
///
/// - Parameter url: Sound file URL.
public init?(url: URL) {
#if os(iOS) || os(tvOS)
_ = Sound.category
#endif
let playersPerSound = max(Sound.playersPerSound, 1)
var myPlayers: [Player] = []
myPlayers.reserveCapacity(playersPerSound)
for _ in 0..<playersPerSound {
do {
let player = try Sound.playerClass.init(contentsOf: url)
myPlayers.append(player)
} catch {
print("SwiftySound initialization error: \(error)")
}
}
if myPlayers.count == 0 {
return nil
}
players = myPlayers
NotificationCenter.default.addObserver(self, selector: #selector(Sound.stopNoteRcv), name: Sound.stopNotificationName, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self, name: Sound.stopNotificationName, object: nil)
}
@objc private func stopNoteRcv() {
stop()
}
private static let stopNotificationName = Notification.Name("com.moonlightapps.SwiftySound.stopNotification")
// MARK: - Main play method
/// Play the sound.
///
/// - Parameter numberOfLoops: Number of loops. Specify a negative number for an infinite loop. Default value of 0 means that the sound will be played once.
/// - Returns: If the sound was played successfully the return value will be true. It will be false if sounds are disabled or if system could not play the sound.
@discardableResult public func play(numberOfLoops: Int = 0, completion: PlayerCompletion? = nil) -> Bool {
if !Sound.enabled {
return false
}
paused = false
counter = (counter 1) % players.count
let player = players[counter]
return player.play(numberOfLoops: numberOfLoops, completion: completion)
}
// MARK: - Stop playing
/// Stop playing the sound.
public func stop() {
for player in players {
player.stop()
}
paused = false
}
/// Pause current playback.
public func pause() {
players[counter].pause()
paused = true
}
/// Resume playing.
@discardableResult public func resume() -> Bool {
if paused {
players[counter].resume()
paused = false
return true
}
return false
}
/// Indicates if the sound is currently playing.
public var playing: Bool {
return players[counter].isPlaying
}
/// Indicates if the sound is paused.
public private(set) var paused: Bool = false
// MARK: - Prepare sound
/// Prepare the sound for playback
///
/// - Returns: True if the sound has been prepared, false in case of error
@discardableResult public func prepare() -> Bool {
let nextIndex = (counter 1) % players.count
return players[nextIndex].prepareToPlay()
}
// MARK: - Convenience static methods
/// Play sound from a sound file.
///
/// - Parameters:
/// - file: Sound file name.
/// - fileExtension: Sound file extension.
/// - numberOfLoops: Number of loops. Specify a negative number for an infinite loop. Default value of 0 means that the sound will be played once.
/// - Returns: If the sound was played successfully the return value will be true. It will be false if sounds are disabled or if system could not play the sound.
@discardableResult public static func play(file: String, fileExtension: String? = nil, numberOfLoops: Int = 0) -> Bool {
if let url = url(for: file, fileExtension: fileExtension) {
return play(url: url, numberOfLoops: numberOfLoops)
}
return false
}
/// Play a sound from URL.
///
/// - Parameters:
/// - url: Sound file URL.
/// - numberOfLoops: Number of loops. Specify a negative number for an infinite loop. Default value of 0 means that the sound will be played once.
/// - Returns: If the sound was played successfully the return value will be true. It will be false if sounds are disabled or if system could not play the sound.
@discardableResult public static func play(url: URL, numberOfLoops: Int = 0) -> Bool {
if !Sound.enabled {
return false
}
var sound = sounds[url]
if sound == nil {
sound = Sound(url: url)
sounds[url] = sound
}
return sound?.play(numberOfLoops: numberOfLoops) ?? false
}
/// Stop playing sound for given URL.
///
/// - Parameter url: Sound file URL.
public static func stop(for url: URL) {
let sound = sounds[url]
sound?.stop()
}
/// Duration of the sound.
public var duration: TimeInterval {
get {
return players[counter].duration
}
}
/// Sound volume.
/// A value in the range 0.0 to 1.0, with 0.0 representing the minimum volume and 1.0 representing the maximum volume.
public var volume: Float {
get {
return players[counter].volume
}
set {
for player in players {
player.volume = newValue
}
}
}
/// Stop playing sound for given sound file.
///
/// - Parameters:
/// - file: Sound file name.
/// - fileExtension: Sound file extension.
public static func stop(file: String, fileExtension: String? = nil) {
if let url = url(for: file, fileExtension: fileExtension) {
let sound = sounds[url]
sound?.stop()
}
}
/// Stop playing all sounds.
public static func stopAll() {
NotificationCenter.default.post(name: stopNotificationName, object: nil)
}
// MARK: - Private helper method
private static func url(for file: String, fileExtension: String? = nil) -> URL? {
return soundsBundle.url(forResource: file, withExtension: fileExtension)
}
}
/// Player protocol. It duplicates `AVAudioPlayer` methods.
public protocol Player: AnyObject {
/// Play the sound.
///
/// - Parameters:
/// - numberOfLoops: Number of loops.
/// - completion: Complation handler.
/// - Returns: true if the sound was played successfully. False otherwise.
func play(numberOfLoops: Int, completion: PlayerCompletion?) -> Bool
/// Stop playing the sound.
func stop()
/// Pause current playback.
func pause()
/// Resume playing.
func resume()
/// Prepare the sound.
func prepareToPlay() -> Bool
/// Create a Player for sound url.
///
/// - Parameter url: sound url.
init(contentsOf url: URL) throws
/// Duration of the sound.
var duration: TimeInterval { get }
/// Sound volume.
var volume: Float { get set }
/// Indicates if the player is currently playing.
var isPlaying: Bool { get }
}
fileprivate var associatedCallbackKey = "com.moonlightapps.SwiftySound.associatedCallbackKey"
public typealias PlayerCompletion = ((Bool) -> ())
extension AVAudioPlayer: Player, AVAudioPlayerDelegate {
public func play(numberOfLoops: Int, completion: PlayerCompletion?) -> Bool {
if let cmpl = completion {
objc_setAssociatedObject(self, &associatedCallbackKey, cmpl, .OBJC_ASSOCIATION_COPY_NONATOMIC)
self.delegate = self
}
self.numberOfLoops = numberOfLoops
return play()
}
public func resume() {
play()
}
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
let cmpl = objc_getAssociatedObject(self, &associatedCallbackKey) as? PlayerCompletion
cmpl?(flag)
objc_removeAssociatedObjects(self)
self.delegate = nil
}
public func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
print("SwiftySound playback error: \(String(describing: error))")
}
}
#if os(iOS) || os(tvOS)
/// Session protocol. It duplicates `setCategory` method of `AVAudioSession` class.
public protocol Session: AnyObject {
/// Set category for session.
///
/// - Parameter category: category.
func setCategory(_ category: AVAudioSession.Category) throws
}
extension AVAudioSession: Session {}
#endif
uj5u.com熱心網友回復:
Sound提供一個需要URL引數的公共初始值設定項。您的包裝器還需要接受一個 URL 引數,以便它可以傳遞它。
init(imageName: String, url: URL) {
self.imageName = imageName
super.init(url: url)
}
創建Sound實體時需要一個 URL。創建SoundWrapper實體時,您將需要相同的 URL。
另請注意,您對所有實體hash的使用效率都很低。0為什么不使用id?
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
uj5u.com熱心網友回復:
你沒有url在任何地方定義,SoundWrapper所以 Swift 編譯器試圖找到在url別處定義的地方。我相信它是在Sound:上找到私有方法private static func url(for file: String, fileExtension: String? = nil) -> URL?并告訴你它是私有的。
所以問題是,您實際上想將什么 url 傳遞給Sound基類初始化程式?
我想您的SoundWrapper初始化程式會更改為如下所示:
init(imageName: String) {
self.imageName = imageName
super.init(url: URL(string: "example.com")!)
// or use a URL defined on your class or somewhere else
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/534954.html
標籤:迅速班级初始化子类超类
