I'm using PanelKit for an iOS app that I'm developing right now.
I have set up a panel, which is a ViewController, and have set up a button in that ViewController. When pressed, I want a picture to be displayed in another ViewController (the main VC that's instantiated when the app is launched). I'm using a delegate for this. However, my delegate function never gets executed. Here's my code:
Delegate:
protocol IMGPickerDelegate: class {
func didFinishPicking(_ imageData: UIImage)
}
Panel ViewController:
class IMGLibraryViewController: UIViewController, PanelContentDelegate {
weak var delegate: IMGPickerDelegate?
#IBAction func fireButton(_ sender: Any) {
let imageSample = UIImage(named: "sample")!
didPickImage(imageDData: imageSample)
}
func didPickImage(imageDData: UIImage) {
delegate?.didFinishPicking(imageDData)
}
Main ViewController:
class ImageDisplayViewController: UIViewController, IMGPickerDelegate {
#IBOutlet weak var imageDisplayViewOutlet: UIImageView!
var imgLibrary = IMGLibraryViewController()
func didFinishPicking(_ imageData: UIImage) {
imageDisplayViewOutlet.image = imageData
}
override func viewDidLoad() {
super.viewDidLoad()
imgLibrary.delegate = self
}
Related
I have a view controller with a UIImage that will be sent to a service. I'm trying to load this image from another view so I'm using delegation. However I cant seem to understand why it will not work. I set some messages to print in the console and I see them right before the call to the method but its not setting the variables and I'm not sure why.
The view are linked via a navigation controller and to return I use popViewController and to go to the other view controller I use a simple show segue from the button. The "confirmation" of the selected image is done with a button that is supposed to send the image to the previous view controller.
This is the main controller with the UIImage I want to set from another view:
import UIKit
class MainNewPost: UIViewController{
override var preferredStatusBarStyle: UIStatusBarStyle{
return.lightContent
}
var imageForUpload: UIImage?
var isImageSelected: Bool = false
#IBOutlet weak var newPostBox: TVUIView!
#IBOutlet weak var newPost: UIButton!
#IBAction func sendNewPost(_ sender: Any) {
var imageId = ""
if isImageSelected {
imageId = UUID().uuidString
ImageWS.sendIamge(imageId: imageId, image: imageForUpload!, {()}, error: {(errorMessate) in
print(errorMessate)
})
}
if (newPostBox.text!.isEmpty) {
Util.showMessage(controller: self, message: "Ingrese un texto", seconds: 5)
} else {
PostWS.newPost({() in
Util.showMessage(controller: self, message: "Enviaste un mensaje!", seconds: 3.0)
}, img: imageId, postBody: newPostBox.text!, personId: PersonBE.shared!.personId, posterName: PersonBE.shared!.displayName, error: {(errorMessage) in print(errorMessage)})
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension MainNewPost: NewPostDelegate {
// This is not executing. I'm calling it since I see the console message by the flag and image dont get set
func selectedImage(_ image: UIImage, _ isImageSelected: Bool) {
self.imageForUpload = image
self.isImageSelected = isImageSelected
}
}
This is the view controller where I select the image:
import UIKit
protocol NewPostDelegate {
func selectedImage(_ image: UIImage, _ isImageSelected: Bool)
}
class imageSelectViewController: UIViewController {
var postDelegate: NewPostDelegate?
#IBOutlet weak var imageView: UIImageView!
var imagePicker: ImagePicker!
override func viewDidLoad() {
super.viewDidLoad()
self.imagePicker = ImagePicker(presentationController: self, delegate: self)
}
#IBAction func showImagePicker(_ sender: UIButton) {
self.imagePicker.present(from: sender)
}
override var preferredStatusBarStyle: UIStatusBarStyle{
return.lightContent
}
#IBAction func clickBtnBack(_ sender: Any){
self.navigationController?.popViewController(animated: true)
}
#IBAction func selectImage(_ sender: Any) {
// I see this message printed in the console
// This button is suposed to send the selected image to the previous view controller
print("In selectImage button")
self.postDelegate?.selectedImage(imageView.image!, true)
self.navigationController?.popViewController(animated: true)
}
}
extension imageSelectViewController: ImagePickerDelegate {
func didSelect(image: UIImage?) {
// I see this message printed in the console
print("In didSelect delegate method")
self.imageView.image = image
self.postDelegate?.selectedImage(image!, true)
}
}
Thanks!
It seems that var postDelegate: NewPostDelegate? in your imageSelectViewController instance remains nil. Which means that even if you're calling a delegate method, there is no delegate specified itself.
In order to fix it you should assign your MainNewPost instance reference to var postDelegate: NewPostDelegate? of your destination imageSelectViewController while preparing for segue.
I'm sandboxing a little timer app.
I'm using this cocoapod
This is my code so far:
import UIKit
import CountdownLabel
class ViewController: UIViewController {
#IBOutlet weak var moreBtn: UIButton!
#IBOutlet weak var lessBtn: UIButton!
#IBOutlet weak var gifview: UIImageView!
#IBOutlet weak var countdownLabel: CountdownLabel!
var mins = 25
override func viewDidLoad() {
super.viewDidLoad()
setupCountdown()
updateTimer(min: mins)
}
// MARK: Buttons
#IBAction func startPressed(_ sender: Any) {
countdownLabel.start()
}
#IBAction func lessPressed(_ sender: Any) {
if(mins > 0){
mins = mins - 5
updateTimer(min: mins)
}
}
#IBAction func morePressed(_ sender: Any) {
mins = mins + 5
updateTimer(min: mins)
}
//MARK: Helper Func
func updateTimer(min: Int){
countdownLabel.setCountDownTime(minutes: 60*Double(min))
}
func setupCountdown(){
countdownLabel.textColor = .black
countdownLabel.font = UIFont(name:"Courier", size:UIFont.labelFontSize)
countdownLabel.animationType = .Evaporate
}
}
Now I want to check if the timer is finished (can use this cocoapod built in function: countdownLabel.isFinished() ).
But I have no clue WHERE and HOW I can check this.
As an example: if(countdownLabel.isFinished()){countdownLabel.text = "Finished"}
Thanks for your help
This code is not tested, I'm just following the document you posted. Seems like you don't need to check if countdownLabel.isFinished(), because there is a delegate method for that which will be called as soon as it is finished.
Set up the delegate in func setupCountdown():
countdownLabel.countdownDelegate = self
Conform the protocol in your view controller:
class ViewController: UIViewController, CountdownLabelDelegate
Add the delegate method in your view controller:
func countdownFinished() {
// This method will be called when the countdown finishes.
// Do whatever you need here.
}
In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.
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)
}
}
I am making an app with a main view controller and a menu button in it. By clicking the button, there is another view controller as the side-menu appearing and then users can choose to go back to the previous main view controller. However, then the menu button doesn't work anymore.
The code for the main view controller is here (not the container view controller):
#objc
protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
class CenterViewController: UIViewController {
#IBOutlet weak var titleLabel: UILabel!
var delegate: CenterViewControllerDelegate?
#IBAction func MenuTapped(sender: AnyObject) {
delegate?.toggleLeftPanel?()
}
// MARK: Button actions
}
You have to take a bool flag to track open/collapse of menu, something like this:
#objc
protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
class CenterViewController: UIViewController {
var _isMenuOpen = false
#IBOutlet weak var titleLabel: UILabel!
var delegate: CenterViewControllerDelegate?
#IBAction func MenuTapped(sender: AnyObject) {
if _isMenuOpen{
delegate?.toggleLeftPanel?()
_isMenuOpen = true
}
else{
delegate?.collapseSidePanels?()
_isMenuOpen = false
}
}
// MARK: Button actions
}
Hope this will help you.