问题
My task is to create an app where I can change and save the background. I wanted to do something more elaborate, so I decided to create a color menu that allows me to change all of the views' backgrounds. Now, I have the menu, and I know how to set and save pictures/ colours in the background. But I don't know how to do it from a view controller to another. Right now my code changes its own view's background. This is my code (I know it's a lot...):
First of all I have 2 extensions that allows me to 1) save the UIColor with user defaults; 2) use rgb colours:
//MARK: - Extension for rgb
extension UIColor {
convenience init(red: Int, green: Int, blue: Int) {
assert(red >= 0 && red <= 255, "Invalid red component")
assert(green >= 0 && green <= 255, "Invalid green component")
assert(blue >= 0 && blue <= 255, "Invalid blue component")
self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
}
convenience init(rgb: Int) {
self.init(
red: (rgb >> 16) & 0xFF,
green: (rgb >> 8) & 0xFF,
blue: rgb & 0xFF
)
}
}
//MARK: - Extension to save colors
extension UserDefaults {
func setColor(color: UIColor?, forKey key: String) {
var colorData: NSData?
if let color = color {
do {
colorData = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) as NSData?
set(colorData, forKey: key)
} catch let error {
print("error archiving color data", error)
}
}
}
func colorForKey(key: String) -> UIColor? {
var color: UIColor?
if let colorData = data(forKey: "Background") {
do{
color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorData)
} catch let error {
print("error unarchivig color data", error)
}
}
return color
}
}
My viewDidLoad:
var defaults = UserDefaults.standard
private let backgroundKey = "backg"
override func viewDidLoad() {
super.viewDidLoad()
//MARK: - Background Settings
let color = defaults.colorForKey(key: backgroundKey)
view.backgroundColor = color
if let backgroundImage = self.getSavedBackgroundImage() {
self.backgroundImageView.image = backgroundImage
}
}
Code that allows me to set a picture as background
@IBOutlet weak var backgroundImageView: UIImageView!
//I KNOW THIS HAS TO BE PUT IN THE VC WHERE I WANT THE BACKGROUND TO BE CHANGED
@IBAction func pictureButton(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = false
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
backgroundImageView.image = image
let _ = self.saveBackgroundImage(image: image)
}
self.dismiss(animated: true, completion: nil)
}
func saveBackgroundImage(image: UIImage) -> Bool {
guard let data = image.pngData() ?? backgroundImageView.image?.pngData() else {
return false
}
guard let directory = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first else {
return false
}
do {
try data.write(to: directory.appendingPathComponent("MainBackgroound.png"))
return true
} catch {
print(error.localizedDescription)
return false
}
}
func getSavedBackgroundImage() -> UIImage? {
if let dir = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first {
return UIImage(contentsOfFile: URL(fileURLWithPath: dir.absoluteString).appendingPathComponent("MainBackgroound.png").path)
}
return nil
}
func deleteImage() {
let fileManager = FileManager.default
let yourProjectImagesPath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("MainBackgroound.png")
if fileManager.fileExists(atPath: yourProjectImagesPath) {
try! fileManager.removeItem(atPath: yourProjectImagesPath)
}
let yourProjectDirectoryPath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("MainBackgroound.png")
if fileManager.fileExists(atPath: yourProjectDirectoryPath) {
try! fileManager.removeItem(atPath: yourProjectDirectoryPath)
}
}
Code that allows me to change background's color
@IBAction func lightPinkButton(_ sender: Any) {
self.deleteImage()
let lightPink = UIColor(red: 255, green: 215, blue: 214).withAlphaComponent(1)
defaults.setColor(color: lightPink, forKey: backgroundKey)
view.backgroundColor = lightPink
}
回答1:
How about you create a base view controller, something like ColorViewController
and in there you have a reference to the background color and then just inherit from that and update it wherever you need.
Like this
ColorViewController
class ColorViewController: UIViewController {
var myBackgroundColor: UIColor = .white
override func viewDidLoad() {
super.viewDidLoad()
}
}
ViewControllerOne
class ViewControllerOne: ColorViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
view.backgroundColor = myBackgroundColor
}
@IBAction func blueButtonTapped(_ sender: Any) {
myBackgroundColor = .blue
view.backgroundColor = myBackgroundColor
}
}
ViewControllerTwo
class ViewControllerTwo: ColorViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
view.backgroundColor = myBackgroundColor
}
@IBAction func purpleButton(_ sender: Any) {
myBackgroundColor = .purple
view.backgroundColor = myBackgroundColor
}
}
回答2:
You should make a Notification
for when your background color changes and for when your background image changes. Any ViewController that is supposed to be themed needs to listen for these notifications and update itself accordingly:
//Make your custom notification
extension Notification.Name {
static let backgroundColorUpdated = Notification.Name("backgroundColorUpdated")
}
//Post the notification when you update backgroundColor
NotificationCenter.default.post(Notification(name: .backgroundColorUpdated, object: nil, userInfo: ["backgroundColor": UIColor.red]))
//Subscribe everywhere that needs to be interested in backgroundColor changes
var token: NSObjectProtocol? // retain token for life of subscription, ie as long as you vc exists
...
token = NotificationCenter.default.addObserver(forName: .backgroundColorUpdated, object: nil, queue: .main) { notification in
guard let backgroundColor = notification.userInfo?["backgroundColor"] as? UIColor else {
return
}
//Do stuff with backgroundColors here
}
来源:https://stackoverflow.com/questions/55401038/swift-how-to-change-views-background-from-other-views