Distinguish which button was clicked in custom UIView - ios

I have a custom class which inherits from UIView inside of which is a UIImageView and button. I'm adding them to my storyboard as UIView and let's say there are two views. I have a delegate and a function which executes when the button is pressed and it works for both UIViews.
My problem is that I don't know how to tell which one was clicked. I need to pass it to another VC and then display suitable image in the UIView which was clicked. I've been thinking of setting ids or something but it does not seem like a very scalable idea.
And I cannot simply add #IBAction because the view is UIView as a whole.
Inside the ViewController I've added them as #IBOutlets:
#IBOutlet weak var view: CustomImageView!
#IBOutlet weak var view2: CustomImageView!
EDIT:
I'm pretty close to creating reusable and scalable solution - I'm just assigning the tag to every object and than passing it and retrieving it. Problem I've encountered is that I would like to access properties of a tag. I didn't find any solution on how to do it.

Solution with delegates
You could add a custom delegate protocol for your custom view
protocol CustomViewDelegate {
func didSelectCustomView(_ view: CustomView)
}
class CustomView: UIView {
#IBOutlet weak var myImageView: UIImageView!
#IBOutlet weak var myButton: UIButton!
weak var delegate: CustomViewDelegate?
#IBAction func buttonPressed(sender: UIButton) {
delegate?.didSelectCustomView(self)
}
}
And in your ViewController you have to check from which view to delegate call comes:
class ViewController: UIViewController {
#IBOutlet weak var view1: CustomView!
#IBOutlet weak var view2: CustomView!
override func viewDidLoad() {
super.viewDidLoad()
view1.delegate = self;
view2.delegate = self;
}
}
extension ViewController: CustomViewDelegate {
func didSelectCustomView(_ view: CustomView) {
if view == view1 {
// view1 clicked
} else {
// view2 clicked
}
}
}
Solution with blocks
Instead of a delegate, you could add an optional block that is executed if the button in the view was clicked:
class CustomView: UIView {
#IBOutlet weak var myImageView: UIImageView!
#IBOutlet weak var myButton: UIButton!
var clicked: ((CustomView) -> Void)?
#IBAction func buttonPressed(sender: UIButton) {
clicked?(self)
}
}
And in your ViewController, you can add the block to your views:
class ViewController: UIViewController {
#IBOutlet weak var view1: CustomView!
#IBOutlet weak var view2: CustomView!
override func viewDidLoad() {
super.viewDidLoad()
view1.clicked = { view in
}
view2.clicked = { view in
}
}
}

Related

Storyboard: Connect IBOutlet to a ViewModel/Model bypassing the ViewController

I have a rather complex form designed in the Interface Builder, having about 20 IBOutlets. The form is split onto multiple sections and is static.
Some of the sections might be enabled while the others are disabled (hidden). After the form has been filled in, the app needs to read all of the values (i.e. IBOutlets such as UITextField) and send them to the server.
I used multiple UIStackViews to design each section of the form, so that they could be easily switched on or off.
It's logical after having such a separation with views to have a model that would reflect the same order.
However, I had to link all of the IBOutlets to the UIViewController subclass, flattening any hierarchy.
What I'm trying to achieve is to link individual Form Section Model with specific View. The controller will be responsible only for enabling/disabling the section. The Form Section Model would actually enable specific labels and StackViews and fill in the form values.
Here is the example code I'd like the interface to look like:
import UIKit
class AddressSection {
#IBOutlet weak var sectionStackView: UIStackView!
#IBOutlet weak var sectionTitleLabel: UILabel!
#IBOutlet weak var addressTextField: UITextField!
#IBOutlet weak var isPrimary: UISwitch!
var isHidden: Bool {
get {
return sectionStackView.isHidden
}
set(newValue) {
sectionStackView.isHidden = newValue
}
}
init(){}
}
class NameSection {
#IBOutlet weak var sectionStackView: UIStackView!
#IBOutlet weak var name: UITextField!
#IBOutlet weak var surname: UITextField!
var isHidden: Bool {
get {
return sectionStackView.isHidden
}
set(newValue) {
sectionStackView.isHidden = newValue
}
}
init(){}
}
class MyViewController: UIViewController {
let name = NameSection()
let address = AddressSection()
override func viewDidLoad() {
super.viewDidLoad()
name.isHidden = false
address.isHidden = true
}
}

Swift 4 Why Delegate is not working

In ViewController :
protocol SearchResultDelegate {
func SendDataToResultController(_ result: [SkelbimasModel])
}
class ViewController: UIViewController, UISearchBarDelegate, CLLocationManagerDelegate, MKMapViewDelegate, AddSkelbimas {
#IBOutlet weak var Label: UILabel!
#IBOutlet weak var SearchBar: UISearchBar!
// #IBOutlet weak var boomFilter: BoomMenuButton!
// #IBOutlet weak var Label: UILabel!
#IBOutlet weak var CategoryView: UIView!
#IBOutlet weak var Map: MKMapView!
#IBOutlet weak var bmb: BoomMenuButton!
var Delegate:SearchResultDelegate?
.........
//ijungimiame lentele su paieskos rezultatais
if(result.count > 1) {
Delegate?.SendDataToResultController(result)
GoToSearchResultVC(result)
}
Function GoToSearchResultVC(result) is called it opens new controller
in that new controller code is :
import UIKit
class SearchResultTableViewController: UITableViewController, SearchResultDelegate {
.....
func SendDataToResultController(_ result: [SkelbimasModel]) {
print("result count: \(result.count)")
results = result
}
But this function is never called.
I used delegates before and was fine. Or i'm just tired and do not see where i did wrong...
Somewhere where you create the ViewController in your code in SearchResultTableViewController you have to set the value of the delegate:
let viewController = ViewController()
viewController.Delegate = self
// present viewController
If you are showing after SearchResultTableViewController from ViewController, isn't it easier to do something like:
func GoToSearchResultVC(result: Any) {
let searchVC = SearchResultTableViewController()
searchVC.results = results
self.present(searchVC, animated: true, completion: nil)
}
Delegate design patter is to receive events from other classes, if you only want to set a variable, this way is better for me
BTW checking your code, you are calling first the delegate before creating the SearchResultsVC

How can I access data from a container view from the parent view controller in swift?

I have a view controller(SignUpController) and it has 1 image view at the top, 2 buttons at the bottom and 1 view as container view. The Container view(InfoRegisterController) contains scrollview with several text fields. Container view doesn’t contain any buttons. So, now i need to access text fields of Container view in the parent view controller (or can say as : I need to access all data from container view to parent view controller) so that I can register the user by clicking Register button at the bottom in SignUpController. I couldn’t solve it through delegates and NSUserDefaults too. So , Please help me to solve this in swift 4 ?
This is the parent view controller:
import UIKit
import Material
class SignUpController: UIViewController {
#IBOutlet weak var backgroundView: UIView!
#IBOutlet weak var register: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
backgroundView.layer.borderWidth = 1
backgroundView.layer.borderColor = UIColor.blue.cgColor
register.layer.cornerRadius = 5
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // No need for semicolon
let tv: InfoRegisterController = self.childViewControllers[0] as!InfoRegisterController
print("Hello")
//tv.viewWillAppear(true)
}
All data must be accessed to this place so that I can post it to server.
#IBAction func Register(_ sender: Any) {
}
} // class end
Container View:
import UIKit
import Material
class InfoRegisterController: UIViewController, UITextFieldDelegate, UITableViewDelegate,UITableViewDataSource,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
#IBOutlet weak var fullName: TextField!
#IBOutlet weak var Email: ErrorTextField!
#IBOutlet weak var Password: TextField!
#IBOutlet weak var verifyPassword: TextField!
#IBOutlet weak var address: TextField!
#IBOutlet weak var permanentAddress: TextField!
#IBOutlet weak var currentAddress: TextField!
#IBOutlet weak var DOB: TextField!
#IBOutlet weak var occupation: TextField!
#IBOutlet weak var selectGender: TextField!
#IBOutlet weak var identificationType: TextField!
#IBOutlet weak var identificationID: TextField!
#IBOutlet weak var issuedDate: TextField!
#IBOutlet weak var issuedDistrict: TextField!
#IBOutlet weak var licenseNumber: TextField!
#IBOutlet weak var fathersName: TextField!
#IBOutlet weak var grandfathersName: TextField!
#IBOutlet weak var mobileNumber: TextField!
override func viewDidLoad() {
super.viewDidLoad()
self.fullName.delegate = self
self.Email.delegate = self
self.Password.delegate = self
self.verifyPassword.delegate = self
self.address.delegate = self
self.permanentAddress.delegate = self
self.currentAddress.delegate = self
self.DOB.delegate = self
self.occupation.delegate = self
self.identificationID.delegate = self
self.issuedDate.delegate = self
self.issuedDistrict.delegate = self
self.licenseNumber.delegate = self
self.fathersName.delegate = self
self.grandfathersName.delegate = self
self.mobileNumber.delegate = self
}
}
Here, I need to access all fields fullName,Email, Password,verifyPassword,address, permanentAddress,currentAddress, DOB,occupation and all others to SignUpController so that I can register new user.
To gain access to the container view from your parent, you will have to pass a reference from the container view to the parent and then use it in the parent view controller, there are many ways to do that and here is one of them.
In your viewDidAppear of InfoRegisterController which is the container view controller add the following code, this method will get a reference of InfoRegisterController into the parent to be used.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let signUpControllerParent = self.parent as! SignUpController
signUpControllerParent.saveContrainerViewRefference(vc: self)
}
Now in SignUpController add a local variable for the coming reference to be saved and used later to get the data from the textfields.
var infoRegisterRefferenceVC : InfoRegisterController?
Add this method also in your parent SignUpController
func saveContrainerViewRefference(vc:InfoRegisterController){
self.infoRegisterRefferenceVC = vc
}
Now you can have access to all the textfields and the methods in the container view from the parent for example:
var fullNameTextField = self.infoRegisterRefferenceVC.fullName.text
This should be it :)
There are several ways to accomplish this. You can use "Delegate" and "Protocol" or prepareForSegue method in parent view controller.
var somePropertyYouWantToAccess: NSString?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourChildViewSegueIdentifier" {
let childVc = segue.destinationViewController as ContainerViewController
self.somePropertyYouWantToAccess = childVc.firstName
}
}

Swift Objects in different class not updating

I need the stepper and label to reset back to 0 at the same time that my variables reset. The problem is the steppers and labels are in a different class and are not resetting when the variables do. I tried using delegates(if someone can show me the best way that would be great) instead of an instance of my view controller, but I can't get anything to work. Thanks for any help in advance.
ViewController:
class ViewController: UIViewController
{
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var blueStepper: UIStepper!
#IBOutlet weak var greenStepper: UIStepper!
#IBOutlet weak var redStepper: UIStepper!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
#IBAction func stepperChange1(sender: UIStepper)
{
circleView1.greenn1 = Int(greenStepper.value);
greenValue.text = Int(sender.value).description;
}
#IBAction func stepperChange2(sender: UIStepper)
{
circleView1.bluee1 = Int(blueStepper.value);
blueValue.text = Int(sender.value).description;
}
}
UIView:
class CircleView: UIView
{
var colors1=ViewController()
func updateStepper
{
if(redd1==Int(red1)&&greenn1==Int(green1)&&bluee1==Int(blue1))
{
redd1=0;
greenn1=0;
bluee1=0;
colors1.redStepper.value=0.0;//
colors1.greenStepper.value=0.0;//
colors1.blueStepper.value=0.0;//
}
}
}
I do not quite understand your code, like the "if" condition in your CircleView, the lack of parameters to the method "updateStepper". I am assuming you just wrote some "swift-pseucode" and I will ignore some parts of it to explain how you could implement a delegate for it. Below is an example code:
import UIKit
protocol CircleViewDelegate: class {
func updateStepper(view: CircleView)
}
class ViewController: UIViewController, CircleViewDelegate{
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var blueStepper: UIStepper!
#IBOutlet weak var greenStepper: UIStepper!
#IBOutlet weak var redStepper: UIStepper!
var circleViewDelegate: CircleView!
override func viewDidLoad() {
super.viewDidLoad()
circleViewDelegate = circleView1
circleViewDelegate!.delegate = self
}
func updateStepper(view: CircleView) {
//code you want to execute when you call updateStepper() in the CircleView()
}
}
class CircleView: UIView {
weak var delegate: CircleViewDelegate?
func updateStepper() {
//whenever you want your viewController to updated other views based
//on a condition inside an element like UIView, you can use a delegate
//this way, your code is executed by the ViewController whenever you want
delegate?.updateStepper(self)
}
}
A callback in your UIView must be set to call "updateStepper" when you want. Unfortunately, I didn't quite understand the time it should be called according to your question.
I hope this helps!
Have you tried NSNotification?
If it's always going to reset to zero, then create a func without the if statement in CircleView:
func resetStepper(not: NSNotification) {
r1 = 0
g1 = 0
b1 = 0
c1.rStep.value = 0.0
c1.bStep.value = 0.0
c1.gStep.value = 0.0
}
Also in CircleView's createView func, add:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "resetStepper:", name: "ResetStepper", object: nil)
Then in the view controller, post a notification from whichever button is calling it.
#IBAction func callReset(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("ResetStepper", anObject: nil)
}
That will send the notification that CircleView is listening for to call the function.
Hope that works for you.

How to access IBoutlet of one view controller in another view controller using swift?

How can I change the label text to textfield text?
This source code change the label text to textfield text which are in the same class, but I want to change the label in different class.
label is in the ViewController.swift and textfield is in the ProfileViewController.swift
This is the source code:
ViewController.swift
import UIKit
import Social
class ViewController: UIViewController, SideBarDelegate {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var hedgeImage: UIImageView!
#IBOutlet weak var hideView: UIView!
var sideBar:SideBar = SideBar()
override func viewDidLoad() {
}
ProfileViewController.swift
class ProfileViewController: UIViewController {
#IBOutlet weak var ouput: UILabel!
#IBOutlet weak var name: UITextField!
#IBAction func nameChange(sender: AnyObject) {
ouput.text = name.text
}
}
One of the many ways to do this: You can add an observer to textField and update a variable that holds the name text which then you can send to the new vc in prepare for segue. Here is a sample
class ViewController: UIViewController {
var nameRecieved:String?
#IBOutlet weak var name: UILabel!{
didSet{
if nameRecieved != nil { name.text = nameRecieved }
}
}
}
class ProfileViewController: UIViewController, UITextFieldDelegate {
var nameHolder:String?
#IBOutlet weak var name: UITextField! { didSet{ name.delegate = self } }
//notification
var nameTextFieldObserver: NSObjectProtocol?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let center = NSNotificationCenter.defaultCenter()
let q = NSOperationQueue.mainQueue()
nameTextFieldObserver = center.addObserverForName(UITextFieldTextDidChangeNotification, object: name, queue: q){
notification in
self.nameHolder = self.name.text
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if nameTextFieldObserver != nil { NSNotificationCenter.defaultCenter().removeObserver(nameTextFieldObserver!) }
}
//Here you can pass the value of nameHolder to the other vc's var nameRecieved in your prepare for segue
}
You need to catch hold on to your ViewController object and pass the values. Something like this:
#IBAction func nameChange() {
ouput.text = name.text
// given both your views have a same parent
// and this view is the first child
let myViewController = parentViewController!.childViewControllers[1]
myViewController.name.text = name.text
myViewController.printLabelText()
}

Resources