I'm attempting to create a scene in Swift in which an object (object is a button) is clicked and it creates a pop-up window above the clicked building. I thought i had it down until it gave me an error saying, unexpected nil value when unwrapping an optional or something nasty like that. Here's the code. Thanks for any fixes you may have.
import UIKit
class BuildingUI {
//props
enum buildingTypes {
case residential
case commercial
}
var xValue : CGFloat = 0
var width : CGFloat = 0
//methods
func createBuildingView(xValue : CGFloat, width : CGFloat, buildingType : buildingTypes) {
self.xValue = (xValue+(width/2))
playScreenIns.BuildingView1.frame = CGRect(x: self.xValue, y: playScreenIns.PlayView.frame.height/10, width: (playScreenIns.PlayView.frame.width/5)*3, height: playScreenIns.PlayView.frame.height/4)
playScreenIns.BuildingView1.backgroundColor = UIColor.lightGrayColor()
playScreenIns.BuildingView1.layer.borderWidth = 2
playScreenIns.BuildingView1.layer.borderColor = UIColor.darkGrayColor().CGColor
playScreenIns.BuildingView1.layer.cornerRadius = 25
playScreenIns.scroller.addSubview(playScreenIns.BuildingView1)
}
}
var BuildingUIIns = BuildingUI()
import UIKit
class PlayScreen : UIViewController {
#IBOutlet var PlayView: UIView!
var BuildingView1 : UIView!
#IBAction func CityHallPress(sender: UIButton!) {
BuildingUIIns.createBuildingView(sender.frame.origin.x, width: sender.frame.width, buildingType: .residential)
}
}
var playScreenIns = PlayScreen()
After reviewing your code, and commenting. I believe what you want is
BuildingUI should inherit UIView
class BuildingUI: UIView {}
then in your PlayScreen the BuildingView1 should be of type BuildingUI.
But, I am not so sure on what you're trying to do...are you doing this as experimental test in playground?
On a side note, you should name your your classes with a postfix to what kind of class your inheriting from for example
BuildingUI should be BuildingView and PlayScreen should be PlayScreenViewController
you should start your class properties with a lower case BuildingView1 to buildinvView1 like you do in BuildingUI. But its matter of taste I guess!
Related
I've got a complex application where I am using NSSplitView to create various sidebars which can be opened/shut with gravity (ie, drag the splitter bar close enough to the edge and the view closes completely) the same way XCode does it in it's UI.
Utilizing splitView(_:constrainSplitPosition:ofSubviewAt:) works great when the nested view being hidden does not contain a NSTabView / NSTabViewControllerView however if it does the window refuses to close completely leaving the tabView visible.
class ViewController: NSViewController, NSSplitViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
splitView.delegate = self
}
#IBOutlet var splitView: NSSplitView!
#IBOutlet var tabView: NSTabView!
let gravityTolerance: CGFloat = 180.0
func splitView(
_ splitView: NSSplitView,
constrainSplitPosition proposedPosition: CGFloat,
ofSubviewAt dividerIndex: Int
) -> CGFloat {
print("proposed splitter width: \(dividerIndex) => \(proposedPosition)")
var retVal = proposedPosition
if dividerIndex == 0 {
if proposedPosition <= gravityTolerance {
// tabView.isHidden = true
retVal = 0.0
} else {
// tabView.isHidden = false
}
}
return retVal
}
}
Setting the tab view as "isHidden" makes no difference and I'm pretty sure that if I hand code it all it will work fine. But is there some simple fix ( constraints perhaps ) that I'm missing?
From Willeke's comment... works like a charm. Seems there's lots of Apple-specific support for this feature that I didn't know about: ( obviously one could get a lot fancier than this )
func splitView(
_ splitView: NSSplitView,
canCollapseSubview subview: NSView
) -> Bool
{
return true
}
Here is the situation. I have a protocol, and extension of it.
protocol CustomViewAddable {
var aView: UIView { get }
var bView: UIView { get }
func setupCustomView()
}
extension CustomViewAddable where Self: UIViewController {
var aView: UIView {
let _aView = UIView()
_aView.frame = self.view.bounds
_aView.backgroundColor = .grey
// this is for me to observe how many times this aView init.
print("aView: \(_aView)")
return _aView
}
var bView: UIView {
let _bView = UIView(frame: CGRect(x: 30, y: 30, width: 30, height: 30))
_bView.backgroundColor = .yellow
return _bView
}
func setupCustomView() {
view.addSubview(aView);
aView.addSubview(bView);
}
}
And I make a ViewController to conform this protocol then I add this custom 'aView' to my ViewController's view.
class MyVC: UIViewController, CustomViewAddable {
override func viewDidLoad() {
super.viewDidLoad()
setupCustomView()
}
}
I run it. In my console log it prints twice of init and I trying to do something in my custom 'aView' and it failed. (The code I paste above that I simplified so that it'll be very easy to show my intension)
Could anybody to explain why or make a fix to it that I'll be very appreciated.
Because your var aView: UIView is computed variable not store variable,
So every time you call aView, it will create a new UIView,
You can use Associated Objects in NSObject here is some tutorials:
swift-objc-runtime
associated-objects
Hope this may help.
Basically in the way you implemented the setupCustomView method nothing should work because as mentioned in another response you're using a computed property, so this implies that every time you access the property it's created again.
You don't need to use associated-objects or something like that to achieve what you want, you only need to keep the reference of the aView at the beginning avoiding calling it again, in this way:
func setupCustomView() {
let tView = aView // only is computed once
view.addSubview(tView)
tView.addSubview(bView)
}
I hope this help you.
I have found this answer How to check text field input at real time?
This is what I am looking for. However I am having trouble actually implementing this code. Also my current geographical location makes googling almost impossible.
I want to be able to change the background color of the next text field if the correct number is entered into the previous text field. textfieldTwo background color will change to green if the correct value is entered in textFieldOne. If the value is incorrect then nothing will happen. Please help me out. I have two text fields called textFieldOne and textFieldTwo and nothing else in the code.
Just pop this in your main view controller in an empty project (try using iphone 6 on the simulator)
import UIKit
class ViewController: UIViewController {
var txtField:UITextField!
var txtFieldTwo:UITextField!
var rightNumber = 10
override func viewDidLoad() {
super.viewDidLoad()
//txtFieldOne
var txtField = UITextField()
txtField.frame = CGRectMake(100, 100, 200, 40)
txtField.borderStyle = UITextBorderStyle.None
txtField.backgroundColor = UIColor.blueColor()
txtField.layer.cornerRadius = 5
self.view.addSubview(txtField)
//txtFieldTwo
var txtFieldTwo = UITextField()
txtFieldTwo.frame = CGRectMake(100, 150, 200, 40)
txtFieldTwo.borderStyle = UITextBorderStyle.None
txtFieldTwo.backgroundColor = UIColor.blueColor()
txtFieldTwo.layer.cornerRadius = 5
self.view.addSubview(txtFieldTwo)
txtField.addTarget(self, action: "checkForRightNumber", forControlEvents: UIControlEvents.AllEditingEvents)
self.txtField = txtField
self.txtFieldTwo = txtFieldTwo
}
func checkForRightNumber() {
let number:Int? = self.txtField.text.toInt()
if number == rightNumber {
self.txtFieldTwo.backgroundColor = UIColor.greenColor()
} else {
self.txtFieldTwo.backgroundColor = UIColor.blueColor()
}
}
}
EDIT: Adding a version with IBOutlets and IBActions
Note that in this example the IBAction is connected to txtFieldOne on Sent Events / Editing Changed
Also, make sure your Text Fields border colors are set to None. In the storyboard, the way to do this is to choose the left most option with the dashed border around it. That's so you can color the backgrounds. You can use layer.cornerRadius to set the roundness of the border's edges.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtField: UITextField!
#IBOutlet weak var txtFieldTwo: UITextField!
var rightNumber = 10
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func checkForRightNumber(sender: AnyObject) {
let number:Int? = self.txtField.text.toInt()
if number == rightNumber {
self.txtFieldTwo.backgroundColor = UIColor.greenColor()
} else {
self.txtFieldTwo.backgroundColor = UIColor.blueColor()
}
}
}
Below is just a test of delegation.
What I did was, 1) draw a rectangle, 2) set this rectangle's width of line with a delegate, 3) Hope the storyboard could update its display.
There are two questions:
The first is: If I use "testView.widthdelegate = ViewController()" rather than "testView.widthdelegate = self" , the "var widthValue: CGFloat? = widthdelegate?.trueWidth" will be nil, but it should be 50, what's different between "self" and "ViewController()"?
The second is: I still want to update the result of draw in storyboard, where you can see I did a SetNeedDisplay() but no use at all, how could I do it?
View
import UIKit
protocol widthDelegate: class {
var trueWidth: CGFloat { get }
}
#IBDesignable
class TestView: UIView {
weak var widthdelegate: widthDelegate?
override func drawRect(rect: CGRect) {
var widthValue: CGFloat? = widthdelegate?.trueWidth ?? 1.0
rectangle(widthRefer: widthValue!)
println("width in TestView is \(widthdelegate?.trueWidth)" )
}
func rectangle(#widthRefer: CGFloat) -> UIBezierPath{
var rect = UIBezierPath(rect: CGRect(x: bounds.width/2-50, y: bounds.height/2-50, width: 100, height: 100))
rect.lineWidth = widthRefer
rect.stroke()
return rect
}
}
Controller
import UIKit
class ViewController: UIViewController,widthDelegate {
var trueWidth: CGFloat = 50
#IBOutlet var testView: TestView!{
didSet{ //after the storyboard loaded.
// testView.widthdelegate = ViewController()
testView.widthdelegate = self
testView.setNeedsDisplay()
}
}
}
Answer 1:
self is the actual instance of the class the code is in (correct solution)
ViewController() creates a brand new instance of ViewController which is not identical with the instance created in IB (wrong solution)
Answer 2:
Never implement didSet for an IBOutlet because it's never called during initialization. Better use viewDidLoad() for settings
Some other notes:
Please consider the naming convention that class, protocol and enum names start with a capital letter.
The class constraint in the protocol declaration is not needed
I have a very general question about the Initialization in Swift.
Unlike in Objective C it's now possible to call the init() directly at the declaration outside of my functions:
e.g.
class ViewController: UIViewController {
let myView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
myView.frame = getFrame()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
}
func getFrame() -> CGRect {
return CGRectMake(0, 0, 100, 100)
}
}
In Objective C I would have done the initialization in my function.
But what if I want to call an Initializer with parameters which are not set yet?
e.g.
I want to init with a frame which is being calculated in a func()
class ViewController: UIViewController {
//THIS IS NOT WOKRING
let myView: UIView = UIView(frame: getFrame())
override func viewDidLoad() {
super.viewDidLoad()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
}
func getFrame() -> CGRect {
return CGRectMake(0, 0, 100, 100)
}
}
I don't wanna do my initializations at two different places in the Code. Is there any general pattern for the initializations?
So your options for initialisation in swift are numerous. With your current example you cannot use the method getFrame() yet because you do not yet have a reference to self as the ViewController has not get been initialised. Instead you could use:
let myView: UIView = UIView(frame: CGRectMake(0, 0, 100, 100))
As this does not require the reference to self. Alternatively you could lazy instantiation which will get run after self is available (this can only be used with var not let:
lazy var myView: UIView = {
return UIView(frame:self.getFrame())
}()
To answer your question, when using UIKit class where you often don't have control over their instantiation you can keep doing it the same was as you were in objective c and use implicitly unwrapped optionals (to prevent you having to use a ! or ? every time you instantiate a variable, e.g.:
var myView: UIView!
override func viewDidLoad(){
super.viewDidLoad();
myView = UIView(frame:getFrame())
}
This however does not work with let constants, as they need to be assigned either immediately or in the constructor of the object. Hope this helps.