Building UIButton With Border Initialization - ios

I'm trying to initialize a custom UIButton class, but I'm doing it wrong and not sure how to implement this. I built a custom UIButton class and initialized it in an IBOutlet. The button works fine but none of the properties that I set are displayed.
import UIKit
import Foundation
class WBCircleButton : UIButton {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderColor = UIColor.blueColor().CGColor
self.layer.borderWidth = 2
self.layer.cornerRadius = self.frame.size.width / 2
}
}
class WBMainViewController: UIViewController {
var timeControl = WBTimeController()
var timer = NSTimer()
#IBOutlet weak var timeDisplay: UILabel!
//this doesn't work as expected ?????
#IBOutlet weak var startButton: WBCircleButton!
//********************************************
#IBAction func startTimer(sender: AnyObject) {
timeControl.startTimer()
startButton.setTitle("Stop", forState: UIControlState.Normal)
let aSelector: Selector = "updateTimerDisplay"
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
}
//********************************************
func updateTimerDisplay () {
timeDisplay.text = timeControl.timeLabelText
}
//********************************************
override func viewDidLoad() {
super.viewDidLoad()
}
//********************************************
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

I think you didn't validate something. I created a Xcode Single View Application template project and did the following (check each step to see what is missing for you).
In your project navigator:
Create a new UIViewController class file, name it "WBMainViewController" and set this code in it:
import UIKit
class WBMainViewController: UIViewController {
#IBOutlet weak var startButton: WBCircleButton!
override func viewDidLoad() {
super.viewDidLoad()
startButton.setTitle("Stop", forState: UIControlState.Normal)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Create a new UIButton class file in your project, name it "WBCircleButton" and set the following code in it:
import UIKit
class WBCircleButton: UIButton {
required init(coder decoder: NSCoder) {
super.init(coder: decoder)
layer.borderColor = UIColor.blueColor().CGColor
layer.borderWidth = 2
layer.cornerRadius = self.frame.size.width / 2
}
}
In Interface Builder:
Select your UIViewController scene and set its class to "WBMainViewController" in the Identity Inspector.
Add an UIButton to your scene, set auto layout constraints to it and set your UIButton class to "WBCircleButton" in the Identity Inspector.
Select your ViewController scene and click on the "Show the assistant Editor". Be sure to display your ViewController code and drag your startButton IBOutlet on the UIButton in your Interface Builder scene.
Launch your project.
I was able to display this big button in my ViewController after launching my project in the simulator:

Related

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.

IBoutlet Connected From SuperClass UIView Still Nil

I work with Nibs. I have two screens that will use the "same" UIView component with the same behavior. It's not the same component because in each screen i placed a UIView and made the same configuration, as show on the image.
To solve this and prevent replicate the same code in other classes i wrote one class, that is a UIView subclass, with all the functions that i need.
After that i made my custom class as superclass of these UIView components to inherit the IBOutlets and all the functions.
My custom class is not defined in a Nib, is only a .swift class.
I made all the necessary connections but at run time the IBOutlets is Nil.
The code of my custom class:
class FeelingGuideView: UIView {
#IBOutlet weak var firstScreen: UILabel!
#IBOutlet weak var secondScreen: UILabel!
#IBOutlet weak var thirdScreen: UILabel!
#IBOutlet weak var fourthScreen: UILabel!
private var labelsToManage: [UILabel] = []
private var willShow: Int!
private var didShow: Int!
override init(frame: CGRect) {
super.init(frame: frame)
self.initLabelManagement()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initLabelManagement()
}
private func initLabelManagement() {
self.initLabelVector()
self.willShow = 0
self.didShow = 0
self.setupLabelToShow(label: labelsToManage[0])
self.setupLabels()
}
private func initLabelVector() {
self.labelsToManage.append(self.firstScreen)
self.labelsToManage.append(self.secondScreen)
self.labelsToManage.append(self.thirdScreen)
self.labelsToManage.append(self.fourthScreen)
}
private func setupLabels() {
for label in labelsToManage {
label.layer.borderWidth = 2.0
label.layer.borderColor = UIColor(hex: "1A8BFB").cgColor
}
}
func willShowFeelCell(at index: Int) {
self.willShow = index
if willShow > didShow {
self.setupLabelToShow(label: labelsToManage[willShow])
}
else if willShow < didShow {
for i in didShow ... willShow + 1 {
let label = labelsToManage[i]
self.setupLabelToHide(label: label)
}
}
}
private func setupLabelToShow(label: UILabel) {
label.textColor = UIColor.white
label.backgroundColor = UIColor(hex: "1A8BFB")
}
private func setupLabelToHide(label: UILabel) {
label.textColor = UIColor(hex: "1A8BFB")
label.backgroundColor = UIColor.white
}
}
I found this question similar to mine: Custom UIView from nib inside another UIViewController's nib - IBOutlets are nil
But my UIView is not in a nib.
EDIT:
I overrided the awakeFromNib but it neither enter the method.
More explanation:
My custom class is only superClass of this component:
Which i replicate on two screens.
One screen is a UITableViewCell and the another a UIViewController.
It's all about to manage the behavior of the labels depending on the screen that is showing at the moment on the UICollectionView
When the initLabelVector() function is called at the required init?(coder aDecoder:) it arrises a unwrap error:
The error when try to open the View:
Cannot show the error with the UITableViewCell because it is called at the beginning of the app and don't appear nothing. To show the error with the screen i needed to remove the call of the UITableViewCell.
The UITableViewCell is registered first with the tableView.register(nib:) and after using the tableView.dequeueReusebleCell.
The UIViewController is called from a menu class that way:
startNavigation = UINavigationController(rootViewController: FellingScreenViewController())
appDelegate.centerContainer?.setCenterView(startNavigation, withCloseAnimation: true, completion: nil)
The problem is this code:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initLabelManagement()
}
The trouble is that init(coder:) is too soon to speak of the view's outlets, which is what initLabelManagement does; the outlets are not hooked up yet. Put this instead:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.initLabelManagement()
}
How I arrived at this answer:
As a test, I tried this:
class MyView : UIView {
#IBOutlet var mySubview : UIView!
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
print(#function, self.mySubview)
}
override func awakeFromNib() {
super.awakeFromNib()
print(#function, self.mySubview)
}
}
Here's the output:
init(coder:) nil
awakeFromNib() <UIView: 0x7fc17ef05b70 ... >
What this proves:
init(coder:) is too soon; the outlet is not hooked up yet
awakeFromNib is not too soon; the outlet is hooked up
awakeFromNib is called, despite your claim to the contrary
When the init() from my custom class is called the IBOutlets are not hooked up yet.
So, I created a reference on the parent view and called the iniLabelManagement() from the viewDidLoad() method and everything worked.
Thank you matt, for the help and patience!

Passing data between custom view and view controller

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.

Swift - How to make sure label following slider's thumbnail?

I need to develop custom UISlider using swift. The problem is I don't know how to make sure label following slider's thumbnail like following below:
My code so far
import Foundation
import UIKit
class Slider: UISlider {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 0.2
self.tintColor = UIColor.redColor()
}
}
Probably is worth to take a look at this method, this can't be called directly but you can use it while subclassing
func thumbRectForBounds(_ bounds: CGRect,
trackRect rect: CGRect,
value value: Float) -> CGRect
More info also on this question and here
I had the exact thing for audio files.
The protocol is used to inform the player when user changes the value of the slider.
func updateSliderPosition(position: Float)
is called every half second by the player to change position of the slider current value.
import UIKit
protocol AudioSliderControlDelegate: class
{
func audioSliderControlDdiChangeValue(sender: AudioSliderControl, value: Float)
}
class AudioSliderControl: UIControl
{
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var currentPositionLabel: UILabel!
#IBOutlet weak var totalDurationLabel: UILabel!
weak var delegate: AudioSliderControlDelegate?
private var totalLenght: Double?
override func awakeFromNib()
{
super.awakeFromNib()
backgroundColor = UIColor.clevooOrange()
slider.addTarget(self, action: "sliderValueChanged:", forControlEvents: .ValueChanged)
self.addSeperatorToTop()
}
//MARK:
//MARK: - Setup
func setupForDuration(duration: Double)
{
totalLenght = duration
currentPositionLabel.text = Double(0).formattedDuration()
totalDurationLabel.text = (duration + 0.5).formattedDuration()
slider.setValue(0, animated: false)
}
func updateSliderPosition(position: Float)
{
self.currentPositionLabel.text = (totalLenght! * Double(position)).formattedDuration()
self.slider.value = position
}
//MARK:
//MARK: - Notification
func sliderValueChanged(slider: UISlider)
{
self.currentPositionLabel.text = (totalLenght! * Double(slider.value)).formattedDuration()
delegate?.audioSliderControlDdiChangeValue(self, value: slider.value)
}
}

Create a PopUp in Swift using a custom UIView

I am trying to create a custom UIView and display it as a pop up in my main View using Swift.
My Custom UIView code is
class DatePopUpView: UIView {
var uiView:UIView?
override init() {
super.init()
self.uiView = NSBundle.mainBundle().loadNibNamed("DatePopUpView", owner: self, options: nil)[0] as? UIView
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required override init(frame: CGRect) {
super.init(frame: frame)
}
}
And I am Calling it in my main view as:
#IBAction func date_button_pressed (sender : AnyObject?) {
var popUpView = DatePopUpView()
var centre : CGPoint = CGPoint(x: self.view.center.x, y: self.view.center.y)
popUpView.center = centre
popUpView.layer.cornerRadius = 10.0
let trans = CGAffineTransformScale(popUpView.transform, 0.01, 0.01)
popUpView.transform = trans
self.view .addSubview(popUpView)
UIView .animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
popUpView.transform = CGAffineTransformScale(popUpView.transform, 100.0, 100.0)
}, completion: {
(value: Bool) in
})
}
But popUp is not Coming. I used breakpoint and noticed that value is getting assigned to my popUpView but still it is not displayed on my main View. Please Help
Please Note: I am using StoryBoard for my mainView and custom View i have made using xib.
Without additional description on what you are attempting to do, may I suggest something like the code below? Basically, you can use .hidden feature of a view (or any other control) to show/hide the view. You can set the size and positioning of the view to be popped by using the layout editor.
import UIKit
class ViewController: UIViewController {
var popped = false
var popupBtnTitle = "Show Popup"
#IBAction func popupButton(sender: UIButton) {
popped = !popped
anotherView.hidden = !popped
popupBtnTitle = popped ? "Hide Popup" : "Show Popup"
popupButtonOutlet.setTitle(popupBtnTitle, forState: UIControlState.Normal)
}
#IBOutlet weak var popupButtonOutlet: UIButton!
#IBOutlet weak var anotherView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
popped = false
anotherView.hidden = !popped
popupButtonOutlet.setTitle(popupBtnTitle, forState: UIControlState.Normal)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Resources