How to access views created in viewDivLoad - ios

class ViewController: UIViewController
{
#IBAction func button_down(_ sender: Any)
{
do_something(view: myView);
}
override func viewDidLoad()
{
super.viewDidLoad();
let screen_size: CGRect = UIScreen.main.bounds;
var myView = UIView(frame: CGRect(x: 0, y: screen_size.height/2 - screen_size.width/2, width: screen_size.width, height: screen_size.width));
myView.backgroundColor = UIColor.red;
self.view.addSubview(myView);
}
}
When I try to compile this code, I get an error:
Use of unresolved identifier 'myView'
How can I solve this problem? I have tried declaring myView outside of viewDivLoad, but that's not permitted.

You should definitely do some research on the concept of "scope" in computer programming - it'll be difficult to go too far without it. myView only exists within the scope of viewDidLoad. If you want it to be available outside of that, you can make it a property of the class like so:
class ViewController: UIViewController
{
var myView: UIView!
#IBAction func buttonDown(_ sender: Any)
{
doSomething(view: myView)
}
override func viewDidLoad()
{
super.viewDidLoad()
let screenSize: CGRect = UIScreen.main.bounds
myView = UIView(frame: CGRect(x: 0, y: screenSize.height/2 - screenSize.width/2, width: screenSize.width, height: screenSize.width))
myView.backgroundColor = UIColor.red
self.view.addSubview(myView)
}
}
A couple of other notes:
You don't need semicolons at the end of lines in Swift.
It seems you're using Interface Builder, so I would suggest creating myView that way instead of programmatically.
Function and variable names in Swift should be camel cased - so buttonDown instead of button_down, screenSize instead of screen_size.

Related

Changing class in a UIView (Swift)

I would like to use a button to toggle the contents of a UIView.
I set up two classes, graphClass1 and graphClass2. (The button is in the topView.)
When I click the button, I get the "my button" message from debugPrint, but I don't get the debugPrint messages from within the classes. So, I added setNeedsDisplay but that did not help.
(This is a simplified version - there are actually a lot more classes - which is why I am trying to reuse the same view instead of just creating two separate views.)
how do I get the appropriate class to display in the view?
because graphClass1 creates additional subviews when I toggle back and forth, will the number of graphClass1's subviews just keep growing? If so, how do I remove them when leaving? (I know that self.layer.sublayers = nil or textView.removeAll() would leave them until returning - if they even remove them at all.)
in the button toggle, rather than use a Bool to test which graph, I'd prefer something more intuitive like if currentGraph == GraphClass1 but this gives me the error message: Binary operator '==' cannot be applied to operands of type 'UIView' and 'GraphClass1.Type'. How would do I fix this?
class ViewController: UIViewController {
#IBOutlet var topView: UIView!
#IBOutlet var bottomView: UIView!
#IBOutlet var myButton: UIButton!
var graph1: Bool = true
var currentView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
bottomView = GraphClass1()
setConstraints()
}
#IBAction func myButton(_ sender: Any) {
debugPrint("my button")
if graph1 {
currentView.removeFromSuperview()
currentView = GraphClass2()
cv2.addSubview(currentView)
graph1 = false
}
else {
currentView.removeFromSuperview()
currentView = GraphClass1()
cv2.addSubview(currentView)
graph1 = true
}
cv2.setNeedsDisplay()
}
}
class GraphClass1: UIView {
var textView = UITextView()
override func draw(_ rect: CGRect) {
super.draw(rect)
self.layer.sublayers = nil
textView.removeAll()
createTextView()
debugPrint("inside GraphClass1")
}
func createTextView() {
textView = UITextView(frame: CGRect(x: 8, y: 8, width: 300, height: 100))
textView.text = "Test, this is only a test"
textView.textAlignment = .center
textView.font = UIFont(name: "Courier", size: 16)
textView.backgroundColor = .orange
self.addSubview(textView)
}
}
class GraphClass2: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
debugPrint("inside GraphClass2")
}
}
Instead of changing the UIView() to a GraphClass(), you should add GraphClass() to bottomView() as a subview. To switch out GraphClass1(), you would remove it from bottomView() and set up GraphClass2() as the subview of bottomView(). Removing a view also removes all its subviews.
First, create a bottomView in interface builder, just like the top view. It will be like a place holder. You will have;
#IBOutlet var bottomView: UIView!
In viewDidLoad, you add the specific view as needed with
bottomView.addSubView(myInstanceOfAGraphView.view)
myInstanceOfAGraphView.didMove(toParent: bottomView)
I would create view controllers for each graph view and switch them as needed.
When you need to change, remove its view with;
myInstanceOfAGraphView.view.removeFromSuperview()

How to access the value of a UIControl from another UIView class

Beginner question. I have a UISlider on the storyboard, and in another UIView class besides the ViewController I would like to use the slider's value to change path/shape variables within the makeShape() function.
I have tried:
a) In OtherView, try to directly reference ViewController's global variable val. This results in "unresolved identifier"
b) In ViewController, try to assign mySlider.value to a variable declared in OtherView, otherVal. This results in "Instance member 'otherVal' cannot be used on type 'OtherView'".
Could someone please help me learn how to do this correctly?
ViewController.swift:
class ViewController: UIViewController {
var val: CGFloat = 0.0
#IBOutlet weak var mySlider: UISlider!
#IBOutlet weak var otherView: UIView!
#IBAction func mySliderChanged(_ sender: Any) {
val = CGFloat(mySlider.value)
setOtherVal()
}
func setOtherVal() {
otherView.otherVal = val
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let otherView = OtherView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 100, height: 100)))
self.view.addSubview(otherView)
}
}
OtherView.swift:
class OtherView: UIView {
var path: UIBezierPath!
var otherVal: CGFloat = 0.0
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
makeShape()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func makeShape() {
let width: CGFloat = self.frame.size.width
let height: CGFloat = self.frame.size.height
let path1 = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 50, height: 50)))
let shapeLayer1 = CAShapeLayer()
shapeLayer1.path = path1.cgPath
shapeLayer1.fillColor = UIColor.blue.cgColor
self.layer.addSublayer(shapeLayer1)
shapeLayer1.position = CGPoint(x: otherVal, y: height)
}
}
Right now you're trying to access the otherVal property in ViewController on the class OtherVal which is shared globally through the whole app, not the property on the instance of OtherVal you've created in ViewController.
The simplest fix here is to store the OtherView instance you created in viewDidAppear and use that property to update the view.
class ViewController: UIViewController {
var val: Float = 0.0
#IBOutlet weak var mySlider: UISlider!
// Add a property to hold on to the view after you create it.
var otherView: OtherView?
#IBAction func mySliderChanged(_ sender: Any) {
val = mySlider.value
setOtherVal()
}
func setOtherVal() {
// `val` is a Float and `otherVal` is a `CGFloat` so we have to convert first.
let newValue = CGFloat(self.val)
// Use the property that's holding your view to update with the new value from the slider.
self.otherView?.otherVal = newValue
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let otherView = OtherView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 100, height: 100)))
// Set the property to the view you just created so you can update it later.
self.otherView = otherView
self.view.addSubview(otherView)
}
}
Currently though it doesn't look like you're actually updating OtherView once the new value is set, so you'll want to do something similar there. Keep a reference to the CAShapeLayer you create and update the position when otherVal changes.
Also note viewDidAppear can be called multiple times throughout a view controller's life cycle. If you only want to add the view once, you should use viewDidLoad which will guarantee you have a freshly created self.view to work with.
Edit: As mentioned in the comments, you can also set up a custom view in the storyboard / nib and create an outlet. Right now it doesn't look like your code is set up to work with that though.

Swift class with uibutton.addTarget to UIView not working

I created a new file with the following class:
import Foundation
import UIKit
var Feld = classFeld()
class classFeld {
let button = UIButton()
func createButton() -> UIButton {
button.frame = CGRect(x: 10, y: 50, width: 200, height: 100)
button.backgroundColor=UIColor.black
button.addTarget(self, action: #selector(ButtonPressed(sender:)), for: .touchUpInside)
return button
}
#objc func ButtonPressed(sender: UIButton!) {
button.backgroundColor=UIColor.red
}
}
And this is my ViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
mainview.addSubview(Feld.createButton())
self.view.addSubview(mainview)
}
var mainview=UIView()
}
When I start the app a black button is created but when I click on it it doesnt color red.
If I add the button instead of
mainview.addSubview(Feld.createButton())
to
self.view.addSubview(Feld.createButton())
it works and the button turns red.
May I anyone explain me why? It should make no difference if I add something to self.view or to a subview which is then added to self.view?
Because you need to give it a frame and add it to self.view
var mainview = UIView()
This is because you are just initializing a UIView without giving it any frame and adding it to the main view.
You need to give frame to your mainview also. For example :
class ViewController: UIViewController {
var mainview = UIView()
override func viewDidLoad() {
super.viewDidLoad()
mainview.frame = CGRect(x: 10, y: 50, width: 300, height: 300)
self.view.addSubview(mainview)
mainview.addSubview(Feld.createButton())
}
}
Here is changes on ViewController class and working fine:
class ViewController: UIViewController {
var mainview: UIView!
override func viewDidLoad() {
super.viewDidLoad()
mainview = UIView(frame: self.view.bounds)
mainview.addSubview(Feld.createButton())
self.view.addSubview(mainview)
}
}

Class 'ProductDetailViewController' has no initializers

I am trying to create scrollable imageview. Currently using swift 4.0. While running I am getting "has no initializers" error. I am not sure what code brought this error. I am new to swift and I am unable to track what is going on.
Class 'ProductDetailViewController' has no initializers I am getting this error.
Can any one please tell me how to fix this error.
Entire view controller code is given below. Thanks in advance
import UIKit
import SwiftyJSON
class ProductDetailViewController: UIViewController,UIScrollViewDelegate {
//Outlers
#IBOutlet weak var scrollView: UIScrollView!
//Global variables
var productDict : JSON = []
var arrayOfProductImageUrls : Array<Any> = []
var zoomScroll : UIScrollView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.initialSetup()
self.scrollViewSetup()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
//Mark: - Scrollview Setup
func initialSetup() {
self.scrollView.delegate = self
}
func scrollViewSetup() {
let productsArray : Array = self.arrayOfProductImageUrls;
self.scrollView.backgroundColor = UIColor.white
var frame : CGRect = CGRectMake(0, 0, self.scrollView.frame.size.width, 360)
for (index, value) in productsArray.enumerated() {
//Imageview Setup
let productImageView : UIImageView
let width : NSInteger = Int(frame.width)
productImageView.frame.origin.x = CGFloat(Int (width * index))
productImageView.frame.origin.y = 0
//Scrollview Setup
zoomScroll.frame = frame;
zoomScroll.backgroundColor = UIColor.clear
zoomScroll.showsVerticalScrollIndicator = false
zoomScroll.showsHorizontalScrollIndicator = false
zoomScroll.delegate = self
zoomScroll.minimumZoomScale = 1.0
zoomScroll.maximumZoomScale = 6.0
zoomScroll.tag = index
zoomScroll.isScrollEnabled = true
self.scrollView.addSubview(zoomScroll)
//Setting image
let imageUrl : URL = (productsArray[index] as AnyObject).url
productImageView.sd_setImage(with: imageUrl)
if index < productsArray.count {
productImageView.frame = zoomScroll.bounds
productImageView.contentMode = UIViewContentMode.redraw
productImageView.clipsToBounds = true
productImageView.backgroundColor = UIColor.clear
zoomScroll.addSubview(productImageView)
}
self.scrollView.contentSize = CGSizeMake(CGFloat(frame.origin.x) + CGFloat(width), productImageView.frame.size.height)
}
}
//Mark : Custom methods
func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
return CGRect(x: x, y: y, width: width, height: height)
}
func CGSizeMake(_ width: CGFloat, _ height: CGFloat) -> CGSize {
return CGSize(width: width, height: height)
}
}
#Saurabh Prajapati. Nice and quick catch.
For more, The Swift Programming Language states
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
You can set an initial value for a stored property within an
initializer, or by assigning a default property value as part of the
property’s definition.
Let me explain more, the problem was with scrollView property that does not have a default value. As above mentioned, all variables in Swift must always have a value or nil (make it optional). When you make it optional, you allow it to be nil by default, removing the need to explicitly give it a value or initialize it.
Here we are using ! optional so the default value will be nil
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
}
Setting nil as the default value
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView! = nil
}
Setting the default value, creating an object now it is not nil.
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView = UIScrollView()
}
But we can't-do like that, we are not setting the default value here.
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView
}
I hope it will help.
I had the same echo
it was so easy to solve it by using ! after any variable like this
var ref: DatabaseReference!

UIScrollViewDelegate Not Being Called in Custom Class

When I instantiate and add a scroll view to my self.view in my ViewController class and set the scroll view's delegate to self, the delegate functions get called. As written below:
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var smallView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let width = Double(view.frame.width)
let height = Double(view.frame.height)
let frame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
let scrollView = UIScrollView(frame: frame)
//scrollView.backgroundColor = UIColor.blue
let size = CGSize(width: width + 300, height:1000)
scrollView.contentSize = size
smallView.addSubview(scrollView)
scrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("Gets called.")
}
}
However, when I create a custom class, in this case called, PhotoBooth, and call try to call the delegate function in this custom class, the functions do not get called. Here is my custom class:
import Foundation
import UIKit
class PhotoBooth: NSObject, UIScrollViewDelegate {
private var boothView: UIView
//private var scrollView: UIScrollView
init(view: UIView) {
boothView = view
}
func startSession() {
let width = Double(boothView.frame.width)
let height = Double(boothView.frame.height)
let frame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
let scrollView = UIScrollView(frame: frame)
//scrollView.backgroundColor = UIColor.blue
let size = CGSize(width: width + 300, height:1000)
scrollView.contentSize = size
boothView.addSubview(scrollView)
scrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("paisdjfij")
}
}
And I instantiate the class in my ViewController like so:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var smallView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let photoBooth = PhotoBooth(view: self.view)
photoBooth.startSession()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Any solutions to the problem? Please let me know and thank you so much for your help in advance.
Instead of trying this You should use protocols to call scroll view in custom class.It will look somewhat like this.
CustomClass.h
#protocol CustomDelegate <NSObject>
-(void)customDelegateMethod;
#end
#interface CustomClass : UIScrollView <UIScrollViewDelegate>
{
id<CustomDelegate> delegate
}
CustomClass.m
-(void) methodScrollView
{
[self.delegate customDelegateMethod];
}
ViewController.h
#interface ViewController: UIViewController <CustomDelegate>
{
}
ViewController.m
-(void)makeCustomScrollView
{
CustomClass *objCustom = [[CustomClass alloc] init];
objCustom.delegate = self;
//other stuff
}
-(void)customDelegateMethod
{
}

Resources