Passing data between custom view and view controller - ios

I've got a custom UIControl (TestControl) and want to pass a simple string to a label on the main view. The UIControl (TestControl) sits inside a UIView (CustomView) which has been placed on the storyboard using a view which has got the custom class (CustomView).
What would be a simple implementation that would take care of that?
I noticed that the CustomView is called before the viewDidLoad() in the ViewController.
ViewController.swift
import UIKit
#IBOutlet var someLabel: UILabel!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
TestControl.swift
import UIKit
class TestControl: UIControl {
// Initializer
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.blackColor()
//this is where it would be good if a string could be passed to a label
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
CustomView.swift
import UIKit
#IBDesignable class CustomView: UIView{
#if TARGET_INTERFACE_BUILDER
override func willMoveToSuperview(newSuperview: UIView?) {
let testing: TestControl = TestControl(frame: self.bounds)
self.addSubview(testing)
}
#else
override func awakeFromNib() {
super.awakeFromNib()
let testing: TestControl = TestControl(frame: self.bounds)
self.addSubview(testing)
}
#endif
}

Since you have a control in a storyboard, all you should have to do is to hook up the the didChange event handler to an IBAction in your view controller. This can then interpret this action to do what you want.

Related

Xcode Cannot find type 'HomeView' in scope

Xcode could't find custom UIView class. And if I try to make a simple class like a model it will work normally, but Xcode aslo can't completion code.
This is ViewController:
import UIKit
class HomeViewController: UIViewController {
var hView: HomeView!
var testModel: TestModel!
private func initView(){
self.view.backgroundColor = UIColor.gray
}
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
}
And this is custom class:
import Foundation
import UIKit
import SnapKit
class AboutView: UIView{
var rootOfTop: UIImageView!
var searchBar: UISearchBar!
var messaageButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
installRootOfTop()
installSearchBar()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Install widget.
private func installRootOfTop(){
rootOfTop = UIImageView()
// ...
self.addSubview(rootOfTop)
rootOfTop.snp.makeConstraints{
// ...
}
}
private func installSearchBar(){
searchBar = UISearchBar()
// ...
self.rootOfTop.addSubview(searchBar)
searchBar.snp.makeConstraints{
//...
}
}
} ```[enter image description here][1]
[1]: https://i.stack.imgur.com/zfxZK.jpg
By the way, if I create a new project it also work abnormal, but previous project work normally.
I suspect it's because Xcode temp, but i can't find the project's temp, also i think it's third base, but the new project work abnormal as the same.
Is that the content of your HomeView.swift file?
If true, your class name is wrong.
The file name and the class name don't match for HomeView.swift.
You are declaring an AboutView class inside it, that's why Xcode can't find it.

On awakeFromNib() - Error Instance member 'button' cannot be used on type 'CustomView'

I've created a custom UIView as a .xib file with the UIView having a single button. I load the UIView using the below code.
struct ContentView: View {
var body: some View {
CustomViewRepresentable()
}
}
struct CustomViewRepresentable: UIViewRepresentable {
typealias UIViewType = CustomView
func makeUIView(context: UIViewRepresentableContext<CustomViewRepresentable>) -> CustomView {
let customView = Bundle.main.loadNibNamed("CustomView", owner: nil, options: nil)![0] as! CustomView
return customView
}
func updateUIView(_ uiView: CustomView, context: UIViewRepresentableContext<CustomViewRepresentable>) {
}
}
The custom view has the below code:
class CustomView: UIView {
#IBOutlet weak var button: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override class func awakeFromNib() {
super.awakeFromNib()
// Error - Instance member 'button' cannot be used on type 'CustomView'
button.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
}
#objc func touchUpInside(_ sender: UIButton) {
print("Button clicked")
}
}
I've uploaded the source code to github. Here's the link https://github.com/felixmariaa/AwakeFromNibTest/
This is supposed to work and I'm not sure what is going wrong.
When typing awakeFromNib, I used the autocomplete provided by XCode to complete the function, which resulted in the below code:
override class func awakeFromNib() {
}
Notice the class in the func declaration. This was causing the error:
I removed it and the code worked fine. Thought this would help someone.

How to access a superview's view controller from a child view

I'm creating a UIView that will have a UITableView inside it. I want to set this UITableView delegate and data source equal to the view controller of its superview.
Code:
import UIKit
class AllTasksView: UIView {
let tableview = UITableView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func didMoveToSuperview() {
tableview.delegate = "SUPERVIEW.ViewController"
tableview.dataSource = "SUPERVIEW.ViewController"
}
Is it possible to do so like this? and where is it best to override the UITableview methods? I have already done so in my UIViewController.
Due to separation of concerns, you cannot access a UIViewController from a UIView.
Alternatively, you can use delegation to access the UIViewController
class AllTasksView{
weak var delegate: UITableViewDataSource & UITableviewDelegate?{
didSet{
tableView.delegate = newValue
tableView.dataSource = newValue
}
}
}
ADDITIONAL
class CustomVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
//implement tableview methods here
func viewDidLoad(){
super.viewDidLoad()
let allTasksView = AllTasksView()
view(addSubview: allTasksView)
//add some positioning and size constraints here for allTasksView
allTasksView.delegate = self //this would call the didSet and would automatically setup the allTasksView.tableView's delegate and dataSource to this vc
}
}

How to pass a string value from a class to another on the same viewcontroller

I have created a viewcontroller which consists of two classes. These are displayed below:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var Viewholder: UIImageView!
var txt:String?
override func viewDidLoad() {
super.viewDidLoad()
let vv= TestView(frame: Viewholder.frame)
view.addSubview(vv)
txt= "hello"
let rr = TestView(frame: self.view.frame,textvalue:txt!)
rr.colour = "" // set value
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class TestView: UIView {
var textvalue:String?
init(frame: CGRect,textvalue) {
self.textvalue= textvalue
super.init(frame: frame)
self.sharedLayout()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = frame
self.setupPaths()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func sharedLayout() {
print(textvalue!) // works here
}
func setupPaths() {
print(textvalue!) // doesnt work here (displays nil)
}
How can I make it work so that the value of textvalue in "setupPaths" gives the correct value so that I can then changee the text of a label. I am having trouble getting the setuppath function to display the passed value as it is returning null. This is stopping me from editting the label with a passed value.
You can create a customView like this
class TestView: UIView {
var colour:String?
override init(frame: CGRect) {
super.init(frame: frame)
self.sharedLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.sharedLayout()
}
init(frame: CGRect,colour:String) {
self.colour = colour
super.init(frame: frame)
self.sharedLayout()
}
func sharedLayout() {
}
}
//
You can create it like this
let rr = TestView(frame: self.view.frame,colour:"test")
// rr.colour = "" // set value
You are badly confused about object-oriented programming. In your code:
let vv= TestView(frame: Viewholder.frame)
view.addSubview(vv)
txt= "hello"
let rr = TestView(frame: self.view.frame,textvalue:txt!)
The let vv line creates a TestView object and installs it as a subview of the view controller's content view.
Then the next 2 lines create a NEW instance of TestView and install text into that 2nd view. You then forget about this second view.
This is like buying a new car, setting the station on the radio of the new car, abandoning the new car, and then going home and wondering why the radio station on your existing car didn't change. The two objects are not the same object and settings in one instance of TestView have no effect on the other instance of TestView.

Custom UIView for an UIViewController in Swift

I use code to create the view (with subviews) for UIViewController's this is how I do it:
override loadView()
class MYViewController: UIViewController {
var myView: MyView! { return self.view as MyView }
override func loadView() {
view = MyView()
}
}
and here is how I create my custom view:
class MyView: UIView {
// MARK: Initialization
override init (frame : CGRect) {
super.init(frame : frame)
addSubviews()
setupLayout()
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
// MARK: Build View hierarchy
func addSubviews(){
// add subviews
}
func setupLayout(){
// Autolayout
}
// lazy load views
}
I do this for all my View Controllers and I am looking for more elegant way, because this process is repetitive, so is there any solution for make that generic for example, create a super abstract class, or create an extension for UIViewController and UIView, Protocols ? I am new for swift and I think that Swift can have a better elegant solution with it's modern patterns
If you are wanting to create many different controllers with custom view classes my recommended solution would be along these lines:
First implement a custom view subclass the way you want to be able to use it, here I have used the one you had in your question. You can then subclass this anywhere you need it and just override the relevant methods.
class CustomView: UIView {
// MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
addSubviews()
setupLayout()
}
required init() {
super.init(frame: .zero)
addSubviews()
setupLayout()
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
// MARK: Build View hierarchy
func addSubviews(){
// add subviews
}
func setupLayout(){
// Autolayout
}
}
Then create a generic custom view controller that allows specification of a class as a generic parameter so that you can easily create a controller with a custom view class.
class CustomViewController<T: CustomView>: UIViewController {
var customView: T! { return view as! T }
override func loadView() {
view = T()
}
init() {
super.init(nibName: nil, bundle: nil)
}
}
Then if you wanted to define a new custom view and create a controller that uses it you can simply:
class AnotherCustomView: CustomView { /* Override methods */ }
...
let controller = CustomViewController<AnotherCustomView>()
Boom!
If you wanted you could even typealias this new controller type to make it even more elegant:
class AnotherCustomView: CustomView { /* Override methods */ }
...
typealias AnotherCustomViewController = CustomViewController<AnotherCustomView>
let controller = AnotherCustomViewController()

Resources