I have completed all the needed code for delegate to work. In my viewcontroller:
class ViewController: UIViewControllerCustomViewDelegate
I also have this:
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
func goToNextScene() {
print("GOTOSCENE2")
}
And in my custom view I have this:
import UIKit
protocol CustomViewDelegate: class { // make this class protocol so you can create `weak` reference
func goToNextScene()
}
#available(iOS 10.0, *)
class ImageHeaderView : UIView {
#IBOutlet weak var followme: UISwitch!
#IBOutlet weak var profileImage : UIImageView!
#IBOutlet weak var backgroundImage : UIImageView!
weak var delegate: CustomViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor(hex: "E0E0E0")
self.profileImage.layer.cornerRadius = self.profileImage.frame.size.height / 2
self.profileImage.clipsToBounds = true
self.profileImage.layer.borderWidth = 1
self.profileImage.layer.borderColor = UIColor.white.cgColor
//self.profileImage.setRandomDownloadImage(80, height: 80)
//self.backgroundImage.setRandomDownloadImage(Int(self.frame.size.width), height: 100)
}
#IBAction func followme(_ sender: AnyObject) {
UserDefaults.standard.set(followme.isOn, forKey: "followme")
}
#IBAction func logout(_ sender: AnyObject) {
delegate?.goToNextScene()
print("GOTOSCENE")
}
}
There is are no errors thrown but when I click/tap the button, nothing happens. It just prints "GOTOSCENE".
What I feel is
Your problem is right here
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
I think you have added imageHeaderview from storyboard and in viewdidload you are creating new object of ImageHeaderView and assigning delegate to newly created object.
Try to outlet your ImageHeaderView and assign delegate to outleted object.
Hope this will fix your issue.
Related
I'm new to swift, I would like to change my UIViewController background color from the custom UIToolbar that I already created at XIB. I also already add the custom toolbar to my keyboard. How can i do that?
In my toolbar, I have a button that indicates the color that the user can set to the background color of UIViewController.
My Custom Toolbar
Class for custom toolbar, ColorToolbar.swift
import UIKit
class ColorToolbar: UIToolbar {
#IBOutlet weak var purpleButton: UIBarButtonItem!
#IBOutlet weak var blueButton: UIBarButtonItem!
#IBOutlet weak var greenButton: UIBarButtonItem!
#IBOutlet weak var yellowButton: UIBarButtonItem!
#IBOutlet weak var redButton: UIBarButtonItem!
var makeNotesVC = MakeNotesVC()
var selectedColor : UIColor = UIColor.clear
override func awakeFromNib() {
super.awakeFromNib()
setupButtonColor()
}
#IBAction func colorButtonPressed(_ sender: UIBarButtonItem) {
guard let color = sender.tintColor else {return}
selectedColor = color
print(selectedColor)
makeNotesVC.view.backgroundColor = selectedColor
}
#objc func changeBgColor(){
makeNotesVC.view.backgroundColor = selectedColor
}
private func setupButtonColor(){
purpleButton.tintColor = Constants.TintColorButton.purple
blueButton.tintColor = Constants.TintColorButton.blue
greenButton.tintColor = Constants.TintColorButton.green
yellowButton.tintColor = Constants.TintColorButton.yellow
redButton.tintColor = Constants.TintColorButton.red
}
}
Class for UIViewController that i want to change background color, MakeNotesVC.swift
import UIKit
class MakeNotesVC: UIViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var titleTextfield: UITextField!
#IBOutlet weak var notesText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
addColorbutton()
}
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
let newNote = Note(context: context)
newNote.title = titleTextfield.text
newNote.text = notesText.text
self.navigationController?.popToRootViewController(animated: true)
}
func addColorbutton(){
// TODO : Add Color Options Toolbar
//Register custom toolbar
let toolbar = UINib(nibName: "Toolbar", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! ColorToolbar
toolbar.sizeToFit()
//Add toolbar to keyboard
titleTextfield.inputAccessoryView = toolbar
notesText.inputAccessoryView = toolbar
}
}
Add protocol at your custom tool bar
protocol BackgroundColorControlDelegate: AnyObject {
func changeBackgroundColor(sender:Any, color: UIColor)
}
import UIKit
class ColorToolbar: UIToolbar {
#IBOutlet weak var purpleButton: UIBarButtonItem!
#IBOutlet weak var blueButton: UIBarButtonItem!
#IBOutlet weak var greenButton: UIBarButtonItem!
#IBOutlet weak var yellowButton: UIBarButtonItem!
#IBOutlet weak var redButton: UIBarButtonItem!
var makeNotesVC = MakeNotesVC()
var selectedColor : UIColor = UIColor.clear
weak var delegate: BackgroundColorControlDelegate?
override func awakeFromNib() {
super.awakeFromNib()
setupButtonColor()
}
#IBAction func colorButtonPressed(_ sender: UIBarButtonItem) {
guard let color = sender.tintColor else {return}
selectedColor = color
print(selectedColor)
delegate?.changeBackgroundColor(sender: self, color: color)
}
#objc func changeBgColor(){
makeNotesVC.view.backgroundColor = selectedColor
}
private func setupButtonColor(){
purpleButton.tintColor = Constants.TintColorButton.purple
blueButton.tintColor = Constants.TintColorButton.blue
greenButton.tintColor = Constants.TintColorButton.green
yellowButton.tintColor = Constants.TintColorButton.yellow
redButton.tintColor = Constants.TintColorButton.red
}
}
call delegate from MakeNotesVC
import UIKit
class MakeNotesVC: UIViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var titleTextfield: UITextField!
#IBOutlet weak var notesText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
addColorbutton()
}
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
let newNote = Note(context: context)
newNote.title = titleTextfield.text
newNote.text = notesText.text
self.navigationController?.popToRootViewController(animated: true)
}
func addColorbutton(){
// TODO : Add Color Options Toolbar
//Register custom toolbar
let toolbar = UINib(nibName: "Toolbar", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! ColorToolbar
toolbar.sizeToFit()
toolbar.delegate = self
//Add toolbar to keyboard
titleTextfield.inputAccessoryView = toolbar
notesText.inputAccessoryView = toolbar
}
}
extension MakeNotesVC: BackgroundColorControlDelegate {
func changeBackgroundColor(sender:Any, color: UIColor) {
view.backgroundColor = color
}
}
I want to make error handling pages to all of my view controller through, so when error fetching data error, it's not only showing dialog, but showing a XIB files that contain error message and button to refresh. Like this:
Here's the XIB Class code:
import UIKit
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
#IBAction func didTapTryAgain(_ sender: Any) {
Dialog.showProgressDialog(info: "")
}
}
Here's my base controller code, who handling all the problem.
import Foundation
class BaseViewController: UIViewController {
var uiView = UIView();
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
self.view.addSubview(viewErrorMessage)
}
}
And here's how I call it in another class which I extend BaseViewController, so it can show the error problem globally, without I redeclared again the class:
func onFailedDeleteCart(errorMessage: String) {
getErrorMessage(message: errorMessage)
}
Right now I can pass the error message.
The problem is, I want the Refresh button refreshing current View Controller when I click it. Maybe calling current View Controller's viewDidLoad when I click it will be the nice logic but I don't know how to implement it in XIB class. Anyone can solve out this? Thank you!
Approach: 1
Step:1 Create closure for callback
typealias RefreshBlock = (()->())?
Step:2 Define closure in your UIView class
Step:3 Call closure if user tap refresh button
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
var refreshBlock:RefreshBlock!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
// Step : 3
#IBAction func didTapTryAgain(_ sender: UIButton) {
refreshBlock!()
}
}
Step:4 Assign value in closure when addSubview called
class BaseViewController: UIViewController {
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
viewErrorMessage.refreshBlock = {()
self.viewDidLoad()
print("Refresh Contents")
}
self.view.addSubview(viewErrorMessage)
}
}
Approach: 2
Pass your current UIViewController Reference into UIView class. Refer below code.
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
var currentVC:UIViewController!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
#IBAction func didTapTryAgain(_ sender: UIButton) {
currentVC.viewDidLoad()
}
}
class BaseViewController: UIViewController {
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
viewErrorMessage.currentVC = self
self.view.addSubview(viewErrorMessage)
}
}
The image is not from one VC to another VC.
The problem is displaying it to the image view in the main UIViewController. I have everything hooked up correctly in the Storyboard.
Click here to view my storyboard layout.
Please note some of my unnecessary code has been removed from both ViewController classes.
Here is my first UIViewController:
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var imagePicker: UIImagePickerController!
#IBAction func editPhoto(_ sender: UIButton) {
let ViewController2 = storyboard?.instantiateViewController(withIdentifier: "AddNewEaterySegue") as! ViewController2
ViewController2.imageForEdit = imageView.image!
print(imageView)
print(imageView.image!)
navigationController?.pushViewController(ViewController2, animated: true)
}
#IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
}
}
}
Here is my second UIViewController:
import UIKit
class ViewController2: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var filter : CIFilter!
var imageForEdit = UIImage()
var imagePicker: UIImagePickerController!
#IBOutlet weak var select: UISegmentedControl!
#IBOutlet weak var imageLabel: UIImageView!
#IBOutlet weak var textField: UITextField!
#IBAction func saveButtonPressed(_ sender: UIImage) {
if textField.text == "" {
print("Не все поля заполнены")
} else {
}
performSegue(withIdentifier: "unwindSegueFromViewController", sender: sender)
}
override func viewDidLoad() {
super.viewDidLoad()
imageLabel.image = imageForEdit
print(imageLabel)
// Do any additional setup after loading the view.
}
#IBAction func tappedEnter(_ sender: Any) {
if textField.text?.isEmpty ?? true {
return
} else {
if let texttxt = textField.text {
let data = texttxt.data(using: .ascii, allowLossyConversion: false)
if select.selectedSegmentIndex == 0
{
filter = CIFilter(name: "CICode128BarcodeGenerator")
} else {
filter = CIFilter(name: "CIQRCodeGenerator")
}
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 5, y: 5)
let image = UIImage(ciImage: filter.outputImage!.transformed(by: transform))
imageLabel.image = image
}
}
}
}
When you are pushing to ViewController2 your imageView in ViewController is empty.
Where exactly are you setting the image for imageView?
In your storyboard, the imageView in ViewController is empty and also in the didSet method for imageView in ViewController you have not set any image to the imageView.
Try this in ViewController:
#IBOutlet weak var imageView: UIImageView! {
didSet {
//Add a test image to your project.
imageView.image = UIImage(named: "YOUR_TEST_IMAGE_NAME.jpeg")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
}
}
EDIT: Ok I guess I was not clear on your question. So in order to send the image from "ViewController2" back to "ViewController", you can use many methods but the way I would do it is by using Protocols.
I have done it in a generic way but you can apply this to send any kind of data from one view controller back to the previous view controller.
Try this in ViewController:
import UIKit
protocol ImageViewProtocol{
func sendImageToViewController(theImage: UIImage)
}
class ViewController: UIViewController, ImageViewProtocol {
func sendImageToViewController(theImage: UIImage) {
imageView.image = theImage
}
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let viewcontroller2 = storyboard?.instantiateViewController(withIdentifier: "viewcontroller2") as! ViewController2
viewcontroller2.delegate = self
self.navigationController?.pushViewController(viewcontroller2, animated: true)
}
}
Try this in ViewController2:
import UIKit
class ViewController2: UIViewController {
var delegate: ImageViewProtocol!
#IBOutlet weak var barcodeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
barcodeImageView.image = UIImage(named: "test_image.jpg")
}
#IBAction func saveButtonAction(_ sender: Any) {
delegate.sendImageToViewController(theImage: barcodeImageView.image!)
self.navigationController?.popViewController(animated: true)
}
}
I put a container inside my MainViewController. Then added FirstViewController inside that container
let containerView = UIView()
view.addSubview(containerView)
let controller = FirstViewController()
addChildViewController(controller)
containerView.addSubview(controller.view)
controller.didMove(toParentViewController: self)
There are a UITextField and a UIButton inside FirstViewController. There is a label inside SecondViewController and var passedText = "" to update the UILabel text. I just want to set the UILabel text according to the UITextField text from FirstViewController when I press UIButton and it should leads to SecondViewController as well. This is the UIButton action method from FirstViewController. I'm not using Storyboard
#IBAction func btnPressed(_ sender: Any) {
let secondVC = SecondViewController()
addChildViewController(secondVC)
view.addSubview(secondVC.view)
secondVC.view.frame = view.bounds
secondVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
secondVC.didMove(toParentViewController: self)
secondVC.passedText = inputTextField.text!
}
This is FirstViewController
class FirstViewController: UIViewController {
#IBOutlet weak var inputTextField: UITextField!
let secondVC = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPressed(_ sender: Any) {
addChildViewController(secondVC)
view.addSubview(secondVC.view)
secondVC.view.frame = view.bounds
secondVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
secondVC.didMove(toParentViewController: self)
secondVC.passedText = inputTextField.text!
}
}
This is SecondViewController
class SecondViewController: UIViewController {
var passedText = ""
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = passedText
}
}
Closure
class ParentViewController: UIViewController {
var embeddedViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = FirstViewController()
embedViewController(firstViewController)
firstViewController.passDataClosure = { [weak self] text in
let secondViewController = SecondViewController()
self?.embedViewController(secondViewController)
secondViewController.passedText = text
}
}
func embedViewController(_ viewController: UIViewController) {
removeEmbedded()
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
embeddedViewController = viewController
}
func removeEmbedded() {
embeddedViewController?.view.removeFromSuperview()
embeddedViewController?.didMove(toParentViewController: nil)
embeddedViewController?.removeFromParentViewController()
}
}
-
class FirstViewController: UIViewController {
var passDataClosure: ((String?) -> Void)?
#IBOutlet weak var inputTextField: UITextField!
#IBAction func btnPressed(_ sender: Any) {
passDataClosure?(inputTextField.text)
}
}
-
class SecondViewController: UIViewController {
var passedText: String? {
didSet {
label.text = passedText
}
}
#IBOutlet weak var label: UILabel!
}
Delegate
class ParentViewController: UIViewController, FirstViewControllerDelegate {
var embeddedViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = FirstViewController()
embedViewController(firstViewController)
firstViewController.delegate = self
}
func embedViewController(_ viewController: UIViewController) {
removeEmbedded()
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
embeddedViewController = viewController
}
func removeEmbedded() {
embeddedViewController?.view.removeFromSuperview()
embeddedViewController?.didMove(toParentViewController: nil)
embeddedViewController?.removeFromParentViewController()
}
// MARK: FirstViewControllerDelegate
func firstViewController(_ firstViewController: FirstViewController, pass text: String?) {
let secondViewController = SecondViewController()
embedViewController(secondViewController)
secondViewController.passedText = text
}
}
-
class SecondViewController: UIViewController {
var passedText: String? {
didSet {
label.text = passedText
}
}
#IBOutlet weak var label: UILabel!
}
-
protocol FirstViewControllerDelegate: class {
func firstViewController(_ firstViewController: FirstViewController, pass text: String?)
}
class FirstViewController: UIViewController {
weak var delegate: FirstViewControllerDelegate?
#IBOutlet weak var inputTextField: UITextField!
#IBAction func btnPressed(_ sender: Any) {
delegate?.firstViewController(self, pass: inputTextField.text)
}
}
As far as I can see, this should work:
secondVC.label.text = inputTextField.text
You currently have secondVC.label = inputTextField.text which shouldn't even compile.
you can create a singleton class for your scenario. so that you can access all of your important data from anywhere either from your firstViewController or SecondViewController.
class YourDataClass : NSObject{
var textFieldData = String()
var lblSeconddata = String()
struct Static
{
public static var instance: YourDataClass?
}
class var singleton: YourDataClass
{
if Static.instance == nil
{
Static.instance = YourDataClass()
}
return Static.instance!
}
}
now somewhere in your controller
YourDataClass.Singleton.textFieldData = txtFld.Text
YourDataClass.Singleton. lblSeconddata = lblText.Text
in smame way you can get it anywhere
I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.