How to turn decimal into a fraction - Swift - ios

I have a fraction calculator and when I calculate it, it turns into a decimal. I want to turn it into a fraction. I don't know how to do it in code but I tried separating the calculations but it wouldn't work here is the code with a screenshot of my storyboard https://www.dropbox.com/s/fmapxaou49zap05/iOS%20Simulator%20Screen%20Shot%20Oct%2015%2C%202014%2C%209.37.43%20PM.png?dl=0
//Adding Function
changingSignsLabel.text = "+"
let firstDenomInTextField:Double! = (firstDenominatorTextField.text as NSString).doubleValue
let firstNumInTextField:Double! = (firstNumeratorTextField.text as NSString).doubleValue
let firstWholeInTextField:Double! = (firstWholeNumberTextField.text as NSString).doubleValue
let secondDenomInTextField:Double! = (secondDenominatorTextField.text as NSString).doubleValue
let secondNumInTextField:Double! = (secondNumeratorTextField.text as NSString).doubleValue
let secondWholeInTextField:Double! = (secondWholeNumberTextField.text as NSString).doubleValue
var firstStep = firstDenomInTextField! * firstWholeInTextField! / firstDenomInTextField!
var secondStep = firstStep + firstNumInTextField! / firstDenomInTextField!
var thirdStep = secondDenomInTextField! * secondWholeInTextField! / secondDenomInTextField!
var fourthStep = thirdStep + secondNumInTextField! / secondDenomInTextField!
var calculatedAnswer = (secondStep + fourthStep)
answerLabel.hidden = false
answerLabel.text = "\(calculatedAnswer)"
If you have any questions or concerns please comment them down below
Thank you for your help

For this to work every time, you want to use the Euclidian Algorithm, which in swift looks like this:
func simplify(top:Int, bottom:Int) -> (newTop:Int, newBottom:Int) {
var x = top
var y = bottom
while (y != 0) {
var buffer = y
y = x % y
x = buffer
}
var hcfVal = x
var newTopVal = top/hcfVal
var newBottomVal = bottom/hcfVal
return(newTopVal, newBottomVal)
}
You should just be able to add this to your program, then call it with:
simplify(5,10)
...returning 1 and 2. Hope this helps anyone else trying to find a simpler answer to this question.

Updated Answer
I wrote you a demo app:
Swift Source Code:
Put this inside your view controller.
var numeratorField:UITextField?;
var denominatorField:UITextField?;
var equalButton:UIButton?;
var wholeNumberLabel:UILabel?;
var numeratorLabel:UILabel?;
var denominatorLabel:UILabel?;
var dividerLine:UIView?;
var appName:UILabel?;
var instructions:UILabel?;
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
initViews();
initConstraints();
}
// ---------------------------------------------------------------------
// MARK: Interface Setup
// ---------------------------------------------------------------------
func initViews()
{
self.numeratorField = UITextField();
self.numeratorField?.layer.cornerRadius = 5.0;
self.numeratorField?.layer.borderWidth = 1.0;
self.numeratorField?.textAlignment = NSTextAlignment.Center;
self.denominatorField = UITextField();
self.denominatorField?.layer.cornerRadius = 5.0;
self.denominatorField?.layer.borderWidth = 1.0;
self.denominatorField?.textAlignment = NSTextAlignment.Center;
self.equalButton = UIButton();
self.equalButton?.setTitle("=", forState: UIControlState.Normal);
self.equalButton?.addTarget(self, action: "calculateAnswer", forControlEvents: UIControlEvents.TouchUpInside);
self.equalButton?.setTitleColor(UIColor.darkGrayColor(), forState: UIControlState.Normal);
self.equalButton?.layer.cornerRadius = 5.0;
self.equalButton?.backgroundColor = UIColor.lightGrayColor();
self.wholeNumberLabel = UILabel();
self.wholeNumberLabel?.textAlignment = NSTextAlignment.Right;
self.wholeNumberLabel?.font = UIFont(name: "HelveticaNeue", size: 20);
self.numeratorLabel = UILabel();
self.numeratorLabel?.textAlignment = NSTextAlignment.Center;
self.numeratorLabel?.font = UIFont(name: "HelveticaNeue", size: 14);
self.denominatorLabel = UILabel();
self.denominatorLabel?.textAlignment = NSTextAlignment.Center;
self.denominatorLabel?.font = UIFont(name: "HelveticaNeue", size: 14);
self.dividerLine = UIView();
self.dividerLine?.backgroundColor = self.numeratorLabel!.textColor;
self.dividerLine?.alpha = 0;
self.appName = UILabel();
self.appName?.text = "Fraction App";
self.appName?.font = UIFont(name: "HelveticaNeue-Bold", size: 24);
self.appName?.textAlignment = NSTextAlignment.Center;
self.instructions = UILabel();
self.instructions?.textAlignment = NSTextAlignment.Center;
self.instructions?.text = "Enter a value in the numerator and denominator field, then press the equal sign to see the answer";
self.instructions?.numberOfLines = 0;
self.instructions?.lineBreakMode = NSLineBreakMode.ByWordWrapping;
self.view.addSubview(self.numeratorField!);
self.view.addSubview(self.denominatorField!);
self.view.addSubview(self.equalButton!);
self.view.addSubview(self.wholeNumberLabel!);
self.view.addSubview(self.numeratorLabel!);
self.view.addSubview(self.denominatorLabel!);
self.view.addSubview(self.dividerLine!);
self.view.addSubview(self.appName!);
self.view.addSubview(self.instructions!);
}
func initConstraints()
{
self.appName?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.numeratorField?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.denominatorField?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.equalButton?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.wholeNumberLabel?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.numeratorLabel?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.denominatorLabel?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.dividerLine?.setTranslatesAutoresizingMaskIntoConstraints(false);
self.instructions?.setTranslatesAutoresizingMaskIntoConstraints(false);
var views:NSMutableDictionary = NSMutableDictionary();
views.setValue(self.appName, forKey: "appName");
views.setValue(self.numeratorField, forKey: "numeratorField");
views.setValue(self.denominatorField, forKey: "denominatorField");
views.setValue(self.equalButton, forKey: "equalButton");
views.setValue(self.wholeNumberLabel, forKey: "wholeNumberLabel")
views.setValue(self.numeratorLabel, forKey: "numeratorLabel");
views.setValue(self.denominatorLabel, forKey: "denominatorLabel");
views.setValue(self.dividerLine, forKey: "dividerLine");
views.setValue(self.instructions, forKey: "instructions");
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[appName]|", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[appName]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[instructions]-20-|", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[instructions]-100-|", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-35-[numeratorField(75)]-10-[denominatorField(==numeratorField)]-10-[equalButton]-10-[wholeNumberLabel(>=20)]-2-[dividerLine(15)]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[numeratorField(50)]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[denominatorField(==numeratorField)]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[equalButton(==numeratorField)]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[wholeNumberLabel(==numeratorField)]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[numeratorLabel]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[denominatorLabel]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[dividerLine(1)]", options: nil, metrics: nil, views: views));
self.view.addConstraint(NSLayoutConstraint(item: self.numeratorLabel!, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.wholeNumberLabel, attribute: NSLayoutAttribute.Right, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.denominatorLabel!, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.wholeNumberLabel, attribute: NSLayoutAttribute.Right, multiplier: 1.0, constant: 0.0));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[numeratorLabel(==wholeNumberLabel)]", options: nil, metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[denominatorLabel(==wholeNumberLabel)]", options: nil, metrics: nil, views: views));
self.view.addConstraint(NSLayoutConstraint(item: self.numeratorField!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.denominatorField!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.equalButton!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.wholeNumberLabel!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.dividerLine!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.numeratorLabel!, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.wholeNumberLabel, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
self.view.addConstraint(NSLayoutConstraint(item: self.denominatorLabel!, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.wholeNumberLabel, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0.0));
}
func getLowestCommonDenominator(Numerator numerator:Int, Denominator denominator:Int) -> Int
{
var finalDenominator = denominator;
//println("\(numerator) / \(finalDenominator) = \(numerator % finalDenominator)");
if(numerator % 2 == 0 && finalDenominator % 2 == 0) // even number
{
return self.getLowestCommonDenominator(Numerator: numerator / 2.0, Denominator: finalDenominator / 2.0);
}
else if(numerator % 3 == 0 && finalDenominator % 3 == 0) // odd number
{
return self.getLowestCommonDenominator(Numerator: numerator / 3.0, Denominator: finalDenominator / 3.0);
}
else // prime number, always have a remainder
{
//println("lowest common denominator = \(finalDenominator)");
return finalDenominator;
}
}
func calculateAnswer()
{
// prevent division by zero
if(self.denominatorField?.text == "0")
{
var alert:UIAlertView = UIAlertView(title: "Division By Zero", message: "Divide by zero yields undefined value", delegate: self, cancelButtonTitle: "OK");
alert.show();
return;
}
// clear previous values;
self.wholeNumberLabel?.text = "";
self.numeratorLabel?.text = "";
self.denominatorLabel?.text = "";
var numeratorString:NSString = self.numeratorField!.text;
var denominatorString:NSString = self.denominatorField!.text;
if (numeratorString.length == 0 || denominatorString.length == 0)
{
//var alert:UIAlertView = UIAlertView(title: "Input Missing", message: "Enter a value for numerator and denominator then press the equal sign to get the answer", delegate: nil, cancelButtonTitle: "OK", otherButtonTitles: nil, nil);
var alert:UIAlertView = UIAlertView(title: "Input Missing", message: "Enter a value for numerator and denominator then press the equal sign to get the answer", delegate: nil, cancelButtonTitle: "OK");
alert.show();
}
else
{
var shouldShowWholeParts:Bool = false;
var shouldShowFractionParts:Bool = false;
var numerator:Int = self.numeratorField!.text.toInt()!;
var denominator:Int = self.denominatorField!.text.toInt()!;
var finalNumerator = numerator;
var finalDenominator = self.getLowestCommonDenominator(Numerator: numerator, Denominator: denominator);
if(finalDenominator < denominator)
{
denominator = finalDenominator;
numerator = numerator / finalDenominator;
}
var wholeNumbers:Int = numerator / denominator;
var remainder:Int = numerator % denominator;
//println("wholeNumbers = \(wholeNumbers), remainder = \(remainder)");
//println("\(denominator) % \(remainder) = \(denominator % remainder)");
if(wholeNumbers != 0)
{
shouldShowWholeParts = true;
}
else
{
shouldShowWholeParts = false;
}
if(remainder > 0)
{
// see if we can simply the fraction part as well
if(denominator % remainder == 0) // no remainder means remainder can be simplified further
{
finalDenominator = denominator / remainder;
finalNumerator = remainder / remainder;
}
else
{
finalNumerator = remainder;
finalDenominator = denominator;
}
shouldShowFractionParts = true;
}
else
{
shouldShowFractionParts = false;
}
var answer:NSString?;
if(wholeNumbers > 0 && remainder > 0)
{
answer = NSString(format: "\(wholeNumbers) \(finalNumerator)/\(finalDenominator)");
// prints out whole number and fraction parts
//println("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers) \(finalNumerator)/\(finalDenominator)");
}
else if (wholeNumbers > 0 && remainder == 0)
{
answer = NSString(format: "\(wholeNumbers)");
// prints out whole number only
//println("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers)");
}
else
{
answer = NSString(format: "\(finalNumerator)/\(finalDenominator)");
// prints out fraction part only
//println("Simplified fraction of \(numerator)/\(denominator) = \(finalNumerator)/\(finalDenominator)");
}
if(shouldShowWholeParts)
{
self.wholeNumberLabel?.text = NSString(format: "\(wholeNumbers)");
}
if(shouldShowFractionParts)
{
self.numeratorLabel?.text = NSString(format: "\(finalNumerator)");
self.denominatorLabel?.text = NSString(format: "\(finalDenominator)");
self.dividerLine?.alpha = 1;
}
else
{
self.dividerLine?.alpha = 0;
}
}
}
Original Answer
Is this what you're looking for?
Results:
Simplified fraction of 7/8 = 7/8
Simplified fraction of 12/8 = 1 1/2
Simplified fraction of 5/16 = 5/16
Simplified fraction of 342/23 = 14 20/23
Simplified fraction of 129/72 = 1 57/72
Source Code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
printSimplifiedFraction(Numerator: 7, Denominator: 8);
printSimplifiedFraction(Numerator: 12, Denominator: 8);
printSimplifiedFraction(Numerator: 5, Denominator: 16);
printSimplifiedFraction(Numerator: 342, Denominator: 23);
printSimplifiedFraction(Numerator: 129, Denominator: 72);
}
func printSimplifiedFraction(Numerator numerator: Int, Denominator denominator:Int)
{
var finalNumerator = numerator;
var finalDenominator = denominator;
var wholeNumbers:Int = numerator / denominator;
var remainder:Int = numerator % denominator;
//println("wholeNumbers = \(wholeNumbers), remainder = \(remainder)");
//println("\(denominator) % \(remainder) = \(denominator % remainder)");
if(remainder > 0)
{
// see if we can simply the fraction part as well
if(denominator % remainder == 0) // no remainder means remainder can be simplified further
{
finalDenominator = denominator / remainder;
finalNumerator = remainder / remainder;
}
else
{
finalNumerator = remainder;
finalDenominator = denominator;
}
}
if(wholeNumbers > 0 && remainder > 0)
{
// prints out whole number and fraction parts
println("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers) \(finalNumerator)/\(finalDenominator)");
}
else if (wholeNumbers > 0 && remainder == 0)
{
// prints out whole number only
println("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers)");
}
else
{
// prints out fraction part only
println("Simplified fraction of \(numerator)/\(denominator) = \(finalNumerator)/\(finalDenominator)");
}
}

Unfortunately there is not framework you can import to display a fraction in swift or Xcode. What I would recommend is using the same method you used in your storyboard: Have two UILabelView's, one on top of each other, with a line between them.
Instead of calculating the decimal, use basic math to find the numerator and denominator and display them on the top and bottom of the UILabelView.
This method will require changing your program at a fundamental level, however it will work.

Related

SWIFT Alamofire Graph issue

I am new to IOS development. Please help me with issue. I am not getting data on Y axis. Fetching data through Alamofire using webAPI. Data is coming nil. Same code is working fine with UIViewController table. I am getting the response. But when using in Graph it is not working.
Issue: I am using Alamofire to parse WebAPI. 'SLPercent' is the value which i want to display on Y-axis.But xml value coming as nil.
class ViewController: UIViewController, ScrollableGraphViewDataSource {
var xml = try! XML.parse("")
var graphView: ScrollableGraphView!
var currentGraphType = GraphType.bar
var graphConstraints = [NSLayoutConstraint]()
var label = UILabel()
var reloadLabel = UILabel()
// Data for the different plots
var numberOfDataItems = day
// Data for graphs with a single plot
/*lazy var simpleLinePlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 100, shouldIncludeOutliers: false)
*/
lazy var barPlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 100, shouldIncludeOutliers: false)
// Data for graphs with multiple plots
lazy var blueLinePlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 50)
// lazy var orangeLinePlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 40, shouldIncludeOutliers: false)
// Init
override func viewDidLoad() {
super.viewDidLoad()
let user = "ndbd#gmail.com"
let passwort = "xdc"
var url = URL(string: "https://ceef")
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
configuration.timeoutIntervalForResource = 10
let credentialData = "\(user):\(passwort)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])
let headers = ["Accept": "application/xml","Authorization": "Basic \(base64Credentials)"]
DispatchQueue.main.async {
Alamofire.request(
url!,
method: .get,
parameters: nil,
encoding: URLEncoding.default,
headers:headers)
.responseString
{ response in
debugPrint(response)
print(response.request) // original URL request
print(response.response) // HTTP URL response
print(response.data) // server data
print(response.result) // result of response serialization
if response.result.value != nil
{
self.xml = try! XML.parse(response.result.value!)
}
}
}
// self.CallWebAPI()
// Labels for the x-axis
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let nameOfMonth = dateFormatter.string(from: now)
var xAxisLabels: [String] = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth);
graphView = createMultiPlotGraphOne(self.view.frame)
graphView.topMargin = 200
graphView.bottomMargin = 20
addReloadLabel(withText: "RELOAD")
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "⬅", style: .plain, target: self, action: #selector(backAction))
}
func backAction()
{
//print("Back Button Clicked")
dismiss(animated: true, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewDidLoad()
// self.CallWebAPI()
graphView = createMultiPlotGraphOne(self.view.frame)
graphView.topMargin = 200
graphView.bottomMargin = 20
addReloadLabel(withText: "RELOAD")
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidLoad()
graphView = createMultiPlotGraphOne(self.view.frame)
graphView.topMargin = 200
graphView.bottomMargin = 20
addReloadLabel(withText: "RELOAD")
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
}
// Implementation for ScrollableGraphViewDataSource protocol
// #########################################################
// You would usually only have a couple of cases here, one for each
// plot you want to display on the graph. However as this is showing
// off many graphs with different plots, we are using one big switch
// statement.
func value(forPlot plot: Plot, atIndex pointIndex: Int) -> Double {
switch("bar") {
// Data for the graphs with a single plot
case "bar":
return barPlotData[pointIndex]
default:
return 30
}
}
func label(atIndex pointIndex: Int) -> String {
// Ensure that you have a label to return for the index
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let nameOfMonth = dateFormatter.string(from: now)
var xAxisLabels: [String] = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth);
return xAxisLabels[pointIndex]
}
func numberOfPoints() -> Int {
return numberOfDataItems!
}
// Creating Different Kinds of Graphs
// min: 0
// max: 100
// Will not adapt min and max reference lines to range of visible points
private func createBarGraph(_ frame: CGRect) -> ScrollableGraphView {
let graphView = ScrollableGraphView(frame: frame, dataSource: self)
graphView.topMargin = 200
graphView.bottomMargin = 20
// Setup the plot
let barPlot = BarPlot(identifier: "bar")
barPlot.barWidth = 25
barPlot.barLineWidth = 1
barPlot.barLineColor = UIColor.colorFromHex(hexString: "#777777")
barPlot.barColor = UIColor.colorFromHex(hexString: "#555555")
barPlot.adaptAnimationType = ScrollableGraphViewAnimationType.elastic
barPlot.animationDuration = 1.5
// Setup the reference lines
let referenceLines = ReferenceLines()
referenceLines.referenceLineLabelFont = UIFont.boldSystemFont(ofSize: 8)
referenceLines.referenceLineColor = UIColor.white.withAlphaComponent(0.2)
referenceLines.referenceLineLabelColor = UIColor.white
referenceLines.dataPointLabelColor = UIColor.white.withAlphaComponent(0.5)
// Setup the graph
graphView.backgroundFillColor = UIColor.colorFromHex(hexString: "#333333")
graphView.shouldAnimateOnStartup = true
graphView.rangeMax = 100
graphView.rangeMin = 0
// Add everything
graphView.addPlot(plot: barPlot)
graphView.addReferenceLines(referenceLines: referenceLines)
return graphView
}
fileprivate func createMultiPlotGraphOne(_ frame: CGRect) -> ScrollableGraphView {
let graphView = ScrollableGraphView(frame: frame, dataSource: self)
graphView.topMargin = 200
graphView.bottomMargin = 20
// Setup the first plot.
let blueLinePlot = LinePlot(identifier: "multiBlue")
blueLinePlot.lineColor = UIColor.colorFromHex(hexString: "#16aafc")
blueLinePlot.adaptAnimationType = ScrollableGraphViewAnimationType.elastic
// dots on the line
let blueDotPlot = DotPlot(identifier: "multiBlueDot")
blueDotPlot.dataPointType = ScrollableGraphViewDataPointType.circle
blueDotPlot.dataPointSize = 5
blueDotPlot.dataPointFillColor = UIColor.colorFromHex(hexString: "#16aafc")
blueDotPlot.adaptAnimationType = ScrollableGraphViewAnimationType.elastic
// Setup the reference lines.
let referenceLines = ReferenceLines()
referenceLines.referenceLineLabelFont = UIFont.boldSystemFont(ofSize: 8)
referenceLines.referenceLineColor = UIColor.white.withAlphaComponent(0.2)
referenceLines.referenceLineLabelColor = UIColor.white
referenceLines.relativePositions = [0, 0.2, 0.4, 0.6, 0.8, 1]
referenceLines.dataPointLabelColor = UIColor.white.withAlphaComponent(1)
// Setup the graph
graphView.backgroundFillColor = UIColor.colorFromHex(hexString: "#333333")
graphView.dataPointSpacing = 80
graphView.shouldAnimateOnStartup = true
graphView.shouldAdaptRange = true
graphView.shouldRangeAlwaysStartAtZero = true
// Add everything to the graph.
graphView.addReferenceLines(referenceLines: referenceLines)
graphView.addPlot(plot: blueLinePlot)
graphView.addPlot(plot: blueDotPlot)
return graphView
}
// Constraints and Helper Functions
// ################################
private func setupConstraints() {
self.graphView.translatesAutoresizingMaskIntoConstraints = false
graphConstraints.removeAll()
let topConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
let rightConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.right, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
let leftConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.left, multiplier: 1, constant: 0)
graphConstraints.append(topConstraint)
graphConstraints.append(bottomConstraint)
graphConstraints.append(leftConstraint)
graphConstraints.append(rightConstraint)
self.view.addConstraints(graphConstraints)
}
// Adding and updating the graph switching label in the top right corner of the screen.
private func addLabel(withText text: String) {
label.removeFromSuperview()
label = createLabel(withText: text)
label.isUserInteractionEnabled = true
let rightConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.right, multiplier: 1, constant: -20)
let topConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 80)
let heightConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 40)
let widthConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: label.frame.width * 1.5)
let tapGestureRecogniser = UITapGestureRecognizer(target: self, action: #selector(didTap))
label.addGestureRecognizer(tapGestureRecogniser)
self.view.insertSubview(label, aboveSubview: reloadLabel)
self.view.addConstraints([rightConstraint, topConstraint, heightConstraint, widthConstraint])
}
private func addReloadLabel(withText text: String) {
reloadLabel.removeFromSuperview()
reloadLabel = createLabel(withText: text)
reloadLabel.isUserInteractionEnabled = true
let leftConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.left, multiplier: 1, constant: 20)
let topConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 80)
let heightConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 40)
let widthConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: reloadLabel.frame.width * 1.5)
let tapGestureRecogniser = UITapGestureRecognizer(target: self, action: #selector(reloadDidTap))
reloadLabel.addGestureRecognizer(tapGestureRecogniser)
self.view.insertSubview(reloadLabel, aboveSubview: graphView)
self.view.addConstraints([leftConstraint, topConstraint, heightConstraint, widthConstraint])
}
private func createLabel(withText text: String) -> UILabel {
let label = UILabel()
label.backgroundColor = UIColor.black.withAlphaComponent(0.5)
label.text = text
label.textColor = UIColor.white
label.textAlignment = NSTextAlignment.center
label.font = UIFont.boldSystemFont(ofSize: 14)
label.layer.cornerRadius = 2
label.clipsToBounds = true
label.translatesAutoresizingMaskIntoConstraints = false
label.sizeToFit()
return label
}
// Button tap events
func didTap(_ gesture: UITapGestureRecognizer) {
currentGraphType.next()
self.view.removeConstraints(graphConstraints)
graphView.removeFromSuperview()
switch(currentGraphType) {
case .bar:
graphView = createBarGraph(self.view.frame)
addReloadLabel(withText: "RELOAD")
addLabel(withText: "BAR")
}
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
}
func reloadDidTap(_ gesture: UITapGestureRecognizer) {
// TODO: Currently changing the number of data items is not supported.
// It is only possible to change the the actual values of the data before reloading.
// numberOfDataItems = 30
// data for graphs with a single plot
barPlotData = self.generateRandomData(self.numberOfDataItems!, max: 100, shouldIncludeOutliers: false)
blueLinePlotData = self.generateRandomData(self.numberOfDataItems!, max: 50)
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let nameOfMonth = dateFormatter.string(from: now)
var xAxisLabels: [String] = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth);
xAxisLabels = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth)
graphView.reload()
}
// Data Generation
private func generateRandomData(_ numberOfItems: Int, max: Double, shouldIncludeOutliers: Bool = true) -> [Double] {
var data = [Double]()
var counter = 1
for _ in 0 ..< numberOfItems
{
for Result in xml["WebAPiResponse","Result"]
{
let SLPercent = Result["SLPercent"].text!;
let Date = Result["DateCST"].text!;
let DateFromService = Int(Date.substring(to:Date.index(Date.startIndex, offsetBy: 2)))
if (counter == DateFromService!)
{
data.append(Double(SLPercent)!)
}
}
data.append(Double(counter))
counter = counter + 1;
}
return data
}
private func generateRandomData(_ numberOfItems: Int, variance: Double, from: Double) -> [Double] {
var data = [Double]()
for _ in 0 ..< numberOfItems {
let randomVariance = Double(arc4random()).truncatingRemainder(dividingBy: variance)
var randomNumber = from
if(arc4random() % 100 < 50) {
randomNumber += randomVariance
}
else {
randomNumber -= randomVariance
}
data.append(randomNumber)
}
return data
}
private func generateSequentialLabels(_ numberOfItems: Int, text: String) -> [String] {
var labels = [String]()
for i in 0 ..< numberOfItems {
labels.append("\(text) \(i+1)")
}
return labels
}
// The type of the current graph we are showing.
enum GraphType {
case bar
mutating func next() {
switch(self) {
case .bar:
self = GraphType.bar
}
}
}
override var prefersStatusBarHidden : Bool {
return true
}
}

Cannot add NSLayoutContraints for a custom UIButton

At a loss for what I am doing wrong, here is my custom UIButton:
import UIKit
class IteratorChevronButton: UIButton {
required init() {
super.init(frame: .zero)
self.setBackgroundImage(UIImage(named: "icon-chevron-right"), for: .normal)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Usage of IteratorChevronButton in a UIView class:
var btnNext: IteratorChevronButton {
let btn = IteratorChevronButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}
func doInit() {
self.addSubview(btnNext)
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0))
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 10))
}
I am getting the following error:
I tried to make btnNext lazy but I get the following error:
Here is the code for my custom UIView class:
import UIKit
import AVFoundation
import RealmSwift
enum PlayerError {
case unknownError
}
class Player: UIView {
let circularSliderVerticalPostionString:String = "75"
let circularSliderWidthString:String = "180"
let circularSliderHeightString = "180"
var circularSliderWidth:CGFloat!
var circularSliderHeight:CGFloat!
let uiImageIconClose = UIImage(named: "icon-close")
var movieDimension: CGSize = CGSize.zero
var imageGenerator: AVAssetImageGenerator!
var duration: CMTime = CMTimeMake(0, 30)
var avPlayerLayer: AVPlayerLayer?
var avPlayer: AVPlayer!
var startedDragging: Bool = false
var ready: Bool = false
var gForce: Double = 0.0
var isInDoublePlayer:Bool = false //used as a User Runtime Define Attribute in DoublePlayerViewController.xib
lazy var canvas: DrawingLayerView = {
let dv = DrawingLayerView()
return dv
}()
//Set this variable to swithch between normal playback and slow mo
var playSlowMo: Bool {
get {
return playerToolBar.playUsingTimer
}
set {
playerToolBar.playUsingTimer = newValue
}
}
//This when set the playback will resume after user stop dragging... I think its worth showing to
//some of the customers, if I were a player I would like it to be like this :)
var continuePlaybackWhenUserStopDragging: Bool {
get {
return playerToolBar.autoPlayWhenStopDragging
}
set {
playerToolBar.autoPlayWhenStopDragging = newValue
}
}
var playbackComlete: ((_ error: PlayerError?) -> Void)? = nil
lazy var controlBarSize: CGSize = {
return CGSize(width: self.bounds.width*3/4, height: 100)
}()
lazy var playerToolBar: PlayerToolBar = {[unowned self] in
let bar = PlayerToolBar(frame: CGRect.zero)
bar.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(bar)
return bar
}()
let controlsBar: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var closeButton: UIButton = {
let btn = ExtendedBoundsButton(type: .custom)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setImage(self.uiImageIconClose, for: UIControlState())
btn.setTitleColor(UIColor.blue, for: UIControlState())
btn.isHidden = true
self.addSubview(btn)
return btn
}()
lazy var progressLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.white
return label
}()
var btnNext: IteratorChevronButton {
let btn = IteratorChevronButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}
lazy var chevronImageRight: UIImageView = {
let image = UIImage(named:"icon-chevron-right")!
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleToFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
lazy var circularSlider: BWCircularSliderView = {
let cs = BWCircularSliderView()
cs.translatesAutoresizingMaskIntoConstraints = false
cs.frame.size.width = self.circularSliderWidth
cs.frame.size.height = self.circularSliderHeight
return cs
}()
var exporter: AVAssetExportSession? = nil
var autoPlay: Bool = false
var progressTimer: Timer?
var movieDidPlay: (()->Void?)? = nil
var onTap: (()-> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
doInit()
}
func doInit() {
self.circularSliderWidth = CGFloat(Int(circularSliderWidthString)!)
self.circularSliderHeight = CGFloat(Int(circularSliderHeightString)!)
self.addSubview(chevronImageRight)
self.addSubview(progressLabel)
self.addSubview(circularSlider)
self.addSubview(btnNext)
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0)
)
self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 10))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(circularSliderVerticalPostionString)-[circularSlider(\(circularSliderWidthString))]", options: [], metrics: nil, views: ["circularSlider": circularSlider]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[circularSlider(\(circularSliderHeightString))]|", options: [], metrics: nil, views: ["circularSlider": circularSlider]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[toolbar(100)]-0-|", options: [], metrics: nil, views: ["toolbar": playerToolBar]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[toolbar]|", options: [], metrics: nil, views: ["toolbar": playerToolBar]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-35-[btn(40)]", options: [], metrics: nil, views: ["btn": closeButton]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[btn(40)]-10-|", options: [], metrics: nil, views: ["btn": closeButton]))
closeButton.addTarget(self, action: #selector(onClose), for: .touchUpInside)
}
func onClose() {
if !ready {
return
}
if let periodicTimeObserver = playerToolBar.periodicTimeObserver {
self.avPlayer.removeTimeObserver(periodicTimeObserver)
}
self.avPlayer.pause()
progressTimer?.invalidate()
playbackComlete?(nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
doInit()
}
override func layoutSubviews() {
super.layoutSubviews()
playerToolBar.isInDoublePlayer = self.isInDoublePlayer
self.circularSlider.isHidden = self.isInDoublePlayer
self.circularSlider.gForce = self.gForce
if let avPlayerLayer = avPlayerLayer {
avPlayerLayer.bounds = self.bounds
avPlayerLayer.position = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2)
playerToolBar.avPlayer = avPlayer
playerToolBar.setupMovieScrollBar()
if autoPlay {
autoPlay = false
play()
}
movieDidPlay?()
}
progressLabel.frame = CGRect(x: frame.size.width/2-100, y: frame.size.height/2-15, width: 200, height: 30)
addSubview(canvas)
addSubview(playerToolBar)
addSubview(closeButton)
canvas.frame = bounds
}
func onExportTimer(_ sender: AnyObject) {
guard let exporter = exporter else {
return
}
progressLabel.text = "Processing " + String(Int(exporter.progress*100) ) + "%"
}
func mergeFiles(_ items: [String], assetWithOnset: String?, mergeComplete: #escaping (_ fileName: String?)->Void) -> Void {
if (assetWithOnset == nil) {
mergeComplete(items.first!)
return
}
let composition = AVMutableComposition()
let track:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
var insertTime = kCMTimeZero
for item in items {
let sourceAsset = AVAsset(url: URL(fileURLWithPath: FileUtility.getPathForFileMovieDirectory(item)))
let tracks = sourceAsset.tracks(withMediaType: AVMediaTypeVideo)
print("\(item) \(sourceAsset.isPlayable)") // print true
print(sourceAsset.isExportable) // print true
print(sourceAsset.isReadable) // print true
if tracks.count > 0 {
let assetTrack:AVAssetTrack = tracks[0] as AVAssetTrack
do {
try track.insertTimeRange(CMTimeRangeMake(kCMTimeZero,sourceAsset.duration), of: assetTrack, at: insertTime)
insertTime = CMTimeAdd(insertTime, sourceAsset.duration)
} catch {
mergeComplete(nil)
return
}
}
}
let fusedFileName = "fused_" + assetWithOnset!
let fusedFilePath = FileUtility.getPathForFileMovieDirectory(fusedFileName)
let fusedFileUrl = URL(fileURLWithPath: fusedFilePath)
do {
//in case the file merging fails, the residual file will cause
//the file export fail everytime as the file exist
try FileManager.default.removeItem(atPath: fusedFilePath)
} catch {
}
exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPreset1280x720)
guard let exporter = exporter else {
return
}
exporter.outputURL = fusedFileUrl
exporter.outputFileType = AVFileTypeQuickTimeMovie
progressTimer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(onExportTimer(_:)), userInfo: nil, repeats: true);
exporter.exportAsynchronously(completionHandler: {
switch exporter.status{
case AVAssetExportSessionStatus.failed:
if exporter.error != nil {
print("AVAssetExportSession failed \(exporter.error!)")
}else{
print("AVAssetExportSession failed for unknown reason")
}
mergeComplete(nil)
case AVAssetExportSessionStatus.cancelled:
if exporter.error != nil {
print("AVAssetExportSession canceled \(exporter.error!)")
}else{
print("AVAssetExportSession canceled for unknown reason")
}
mergeComplete(nil)
default:
do {
let realm = try Realm()
let movieClip = realm.object(ofType: MovieModel.self, forPrimaryKey: assetWithOnset)
try realm.write {
movieClip?.fusedFile = fusedFileName
}
//The files are released based on the usage count
MovieRepository.sharedInstance.release(file: movieClip?.fileName)
MovieRepository.sharedInstance.release(file: movieClip?.nextFile)
MovieRepository.sharedInstance.release(file: movieClip?.prevFile)
} catch {
}
mergeComplete(fusedFileName)
self.progressLabel.text = ""
self.progressLabel.isHidden = true
NotificationUtility.notifyReloadGallery()
}
})
}
func setMovies(_ items: [String], itemWithOnset asset: String?, playbackCompletion completion: #escaping ((_ error: PlayerError?) -> Void)){
playbackComlete = completion
closeButton.isHidden = false
mergeFiles(items, assetWithOnset: asset ) { [weak self] (fileName) in
DispatchQueue.main.async(execute: { () -> Void in
if let fileName = fileName, let strongSelf = self {
let asset = AVAsset(url: URL(fileURLWithPath: FileUtility.getPathForFileMovieDirectory(fileName)))
let avplayerItem = AVPlayerItem(asset: asset)
strongSelf.progressTimer?.invalidate()
strongSelf.progressLabel.removeFromSuperview()
strongSelf.duration = asset.duration
strongSelf.avPlayer = AVPlayer(playerItem: avplayerItem)
if let playerLayer = strongSelf.avPlayerLayer {
playerLayer.removeFromSuperlayer()
}
strongSelf.avPlayerLayer = AVPlayerLayer(player: strongSelf.avPlayer)
strongSelf.avPlayerLayer?.zPosition = -1 //send to back
strongSelf.self.layer.addSublayer(strongSelf.avPlayerLayer!)
NotificationCenter.default.addObserver(strongSelf, selector: #selector(Player.currentFileDidFinish(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: avplayerItem)
print("Duration \(Float(CMTimeGetSeconds(strongSelf.duration)))")
print("Size \(strongSelf.movieDimension)")
strongSelf.ready = true
strongSelf.autoPlay = true;
strongSelf.setNeedsLayout()
}
})
}
}
func setMovie(movieAsset: MovieModel, completion: #escaping ()->Void) {
movieDidPlay = completion
autoPlay = true
playerToolBar.playeBackTimer?.invalidate()
playerToolBar.playeBackTimer = nil
var clipNames: [String]
var assetWithOnset: String? = nil
if let fusedFile = movieAsset.fusedFile {
clipNames = [fusedFile]
} else {
assetWithOnset = movieAsset.fileName
if let nextFile = movieAsset.nextFile {
clipNames = [movieAsset.prevFile!, movieAsset.fileName!, nextFile]
} else {
clipNames = [movieAsset.prevFile!, movieAsset.fileName!]
}
}
self.setMovies(clipNames, itemWithOnset: assetWithOnset, playbackCompletion: { (err) in
})
closeButton.isHidden = true
}
func resolutionSizeForVideo(_ asset:AVAsset) -> CGSize? {
guard let track = asset.tracks(withMediaType: AVMediaTypeVideo).first else { return nil }
let size = track.naturalSize.applying(track.preferredTransform)
return CGSize(width: fabs(size.width), height: fabs(size.height))
}
//MARK: The playback methods
func pause(){
if ready {
playerToolBar.pause()
}
}
func play() {
if ready {
playerToolBar.play()
}
}
func currentFileDidFinish(_ notification: Notification) {
/* if let periodicTimeObserver = playerToolBar.periodicTimeObserver {
self.avPlayer.removeTimeObserver(periodicTimeObserver)
}
progressTimer?.invalidate()
playbackComlete?(error: nil)*/
avPlayer.seek(to: CMTimeMake(0, 30))
avPlayer.rate = 1.0
}
func stop() {
avPlayer?.pause()
avPlayer = nil
avPlayerLayer?.removeFromSuperlayer()
avPlayerLayer = nil
}
deinit {
NotificationCenter.default.removeObserver(self)
}
//MARK:
}
class CollectionViewThumbNailCell: UICollectionViewCell {
lazy var barView: UIView = {
let lbl = UIView()
lbl.contentMode = .scaleToFill
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.backgroundColor = UIColor.white
lbl.layer.cornerRadius = 2
lbl.clipsToBounds = true
return lbl
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(barView)
}
func configureMark(_ big: Bool) {
if big {
barView.frame = CGRect(x: bounds.size.width/2 - 2, y: 2, width: 4, height: bounds.size.height)
} else {
barView.frame = CGRect(x: bounds.size.width/2 - 2, y: bounds.size.height/2+2, width: 4, height: bounds.size.height/2)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I guess error is due to this (your btnNext is not being made properly):
var btnNext: IteratorChevronButton {
let btn = IteratorChevronButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}
use this syntax to make btnNext
var btnNext: IteratorChevronButton = {
let btn = IteratorChevronButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
You instantiate a new button every time you access btnNext. So after adding one instance as a subview you use other instances for creating the constraints. Since those other subviews are no subviews of self the app crashes.
Make it a lazy var to instantiate it only once:
lazy var btnNext: IteratorChevronButton = {
let btn = IteratorChevronButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
You also have to add the width and height constraint to the button itself instead of self:
btnNext.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
btnNext.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
Update:
As Ahmad F stated the lazy keyword is totally optional in this case. You could simply instantiate your button without it if you will definitely use it:
var btnNext: IteratorChevronButton = {
let btn = IteratorChevronButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()

How to add android like toast in iOS?

In android we can add toast directly.
Is their any method to add similar toast in iOS?
I created the transparent view to use as toast but for multiple text sizes i have to create more than one view.
There is no Android type Toast control available in iOS.
If you want to use something like it, you need to customise UIView with UILabel, or use some already created Toast type component, like below:
Android Type Toast custom
You can use MBProgressHUD to show a toast like android. After adding MBProgressHUD you can display a toast by this way
let progressHUD = MBProgressHUD.showAdded(to: self.view, animated: true)
progressHUD.mode = MBProgressHUDMode.text
progressHUD.detailsLabel.text = "Your message here"
progressHUD.margin = 10.0
progressHUD.offset.y = 150.0
progressHUD.isUserInteractionEnabled = false
progressHUD.removeFromSuperViewOnHide = true
progressHUD.hide(animated: true, afterDelay: 3.0)
This is best library I used for showing Toast in iOS apps same as android.
It also has pod support and pod name is pod 'Toast'
And implementation is so simple like
#import <UIView+Toast.h>
in your ViewController and then following line wherever you want to show it
[self.view makeToast:#"YOUR TOAST MESSAGE" duration:TOAST_TIMEOUT position:TOAST_CENTER];
Value for above keys are
#define TOAST_TOP #"CSToastPositionTop"
#define TOAST_CENTER #"CSToastPositionCenter"
#define TOAST_BOTTOM #"CSToastPositionBottom"
#define TOAST_TIMEOUT 2.0
You can use this function to display toast message in iOS. Just create the extension on the view and call this method with the message.
Swift 4
extension UIView {
func displayToast(_ message : String) {
guard let delegate = UIApplication.shared.delegate as? AppDelegate, let window = delegate.window else {
return
}
if let toast = window.subviews.first(where: { $0 is UILabel && $0.tag == -1001 }) {
toast.removeFromSuperview()
}
let toastView = UILabel()
toastView.backgroundColor = UIColor.black.withAlphaComponent(0.7)
toastView.textColor = UIColor.white
toastView.textAlignment = .center
toastView.font = UIFont(name: "Font-name", size: 17)
toastView.layer.cornerRadius = 25
toastView.text = message
toastView.numberOfLines = 0
toastView.alpha = 0
toastView.translatesAutoresizingMaskIntoConstraints = false
toastView.tag = -1001
window.addSubview(toastView)
let horizontalCenterContraint: NSLayoutConstraint = NSLayoutConstraint(item: toastView, attribute: .centerX, relatedBy: .equal, toItem: window, attribute: .centerX, multiplier: 1, constant: 0)
let widthContraint: NSLayoutConstraint = NSLayoutConstraint(item: toastView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: (self.frame.size.width-25) )
let verticalContraint: [NSLayoutConstraint] = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(>=200)-[toastView(==50)]-68-|", options: [.alignAllCenterX, .alignAllCenterY], metrics: nil, views: ["toastView": toastView])
NSLayoutConstraint.activate([horizontalCenterContraint, widthContraint])
NSLayoutConstraint.activate(verticalContraint)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
toastView.alpha = 1
}, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3), execute: {
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
toastView.alpha = 0
}, completion: { finished in
toastView.removeFromSuperview()
})
})
}
}
usage:
just call view.displayToast("Hello World") from UIViewController
there is no ThostView in iOS. we Can custom are use 3rd party libraries.
follow this link -
https://github.com/scalessec/Toast-Swift
There is a 3rd party library that supports customizable toast notification with single line of code. Here is a simple example of it:
import Toast_Swift
...
// basic usage
self.view.makeToast("This is a piece of toast")
// toast with a specific duration and position
self.view.makeToast("This is a piece of toast", duration: 3.0, position: .top)
Toast Swift library
Or else,
If you want to implement by your own. Use below code.
let toastLabel = UILabel(frame: CGRectMake(self.view.frame.size.width/2 - 150, self.view.frame.size.height-100, 300, 35))
toastLabel.backgroundColor = UIColor.blackColor()
toastLabel.textColor = UIColor.whiteColor()
toastLabel.textAlignment = NSTextAlignment.Center;
self.view.addSubview(toastLabel)
toastLabel.text = "hello man..."
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
UIView.animateWithDuration(4.0, delay: 0.1, options: UIViewAnimationOptions.CurveEaseOut, animations: {
toastLabel.alpha = 0.0
})
An attempt to make Suhit Pal's answer above Swift 4 like with a way to remove an existing Toast once a new appears
extension UIView {
private static var toastView: UILabel? = nil
func displayToast(message : String, duration: Double? = 3.0) -> Void {
if UIView.toastView != nil {
UIView.toastView!.removeFromSuperview()
}
UIView.toastView = UILabel()
UIView.toastView!.backgroundColor = UIColor.black.withAlphaComponent(0.7)
UIView.toastView!.textColor = UIColor.white
UIView.toastView!.textAlignment = .center
UIView.toastView!.font = UIFont(name: "Font-name", size: 17)
UIView.toastView!.layer.masksToBounds = true
UIView.toastView!.layer.cornerRadius = 25
UIView.toastView!.text = message
UIView.toastView!.numberOfLines = 0
UIView.toastView!.alpha = 0
UIView.toastView!.translatesAutoresizingMaskIntoConstraints = false
let window = UIApplication.shared.delegate?.window!
window?.addSubview(UIView.toastView!)
let horizontalCenterContraint : NSLayoutConstraint = NSLayoutConstraint(item: UIView.toastView!, attribute: .centerX, relatedBy: .equal, toItem: window, attribute: .centerX, multiplier: 1, constant: 0)
let widthContraint: NSLayoutConstraint = NSLayoutConstraint(item: UIView.toastView!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: (self.frame.size.width-25) )
let verticalContraint: [NSLayoutConstraint] = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(>=200)-[loginView(==50)]-68-|", options: [.alignAllCenterX, .alignAllCenterY], metrics: nil, views: ["loginView": UIView.toastView!])
NSLayoutConstraint.activate([horizontalCenterContraint, widthContraint])
NSLayoutConstraint.activate(verticalContraint)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
UIView.toastView!.alpha = 1
}, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + duration!) {
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
UIView.toastView!.alpha = 0
}, completion: { (_) in
UIView.toastView!.removeFromSuperview()
})
}
}
}
Here is my latest version of this. It allows to cancel an existing toast by just pushing a new. It also allows to provide an optional "reverseColors" parameter, which writes white on black instead default black on white. The toast adapts to the text size with a little margin left and right.
extension UIView {
private static var toastView: UILabel? = nil
private static var toastViewCancelTask : DispatchWorkItem?
func displayToast(message : String, duration: Double, reverseColors: Bool? = false) -> Void {
if UIView.toastView != nil {
UIView.toastView!.removeFromSuperview()
UIView.toastViewCancelTask?.cancel()
}
UIView.toastView = UILabel()
UIView.toastViewCancelTask = DispatchWorkItem {
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
UIView.toastView!.alpha = 0
}, completion: { (_) in
UIView.toastView!.removeFromSuperview()
})
}
let toastView = UIView.toastView!
print(message)
if reverseColors != nil && reverseColors! {
toastView.backgroundColor = UIColor.black.withAlphaComponent(0.7)
toastView.textColor = UIColor.white
}
else {
toastView.backgroundColor = UIColor.white.withAlphaComponent(0.7)
toastView.textColor = UIColor.black
}
toastView.textAlignment = .center
toastView.font = UIFont.systemFont(ofSize: 17)
toastView.layer.masksToBounds = true
toastView.layer.cornerRadius = 12
toastView.text = message
toastView.numberOfLines = 0
toastView.alpha = 0
toastView.sizeToFit()
toastView.translatesAutoresizingMaskIntoConstraints = false
let width = toastView.frame.size.width + 100 > self.frame.size.width ? self.frame.size.width - 100 : toastView.frame.size.width + 100
let window = UIApplication.shared.delegate?.window!
window?.addSubview(toastView)
let horizontalCenterContraint : NSLayoutConstraint = NSLayoutConstraint(item: toastView, attribute: .centerX, relatedBy: .equal, toItem: window, attribute: .centerX, multiplier: 1, constant: 0)
let widthContraint: NSLayoutConstraint = NSLayoutConstraint(item: toastView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: width )
let verticalContraint: [NSLayoutConstraint] = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(>=200)-[loginView(==50)]-30-|", options: [.alignAllCenterX, .alignAllCenterY], metrics: nil, views: ["loginView": toastView])
NSLayoutConstraint.activate([horizontalCenterContraint, widthContraint])
NSLayoutConstraint.activate(verticalContraint)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
toastView.alpha = 0.8
}, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + duration , execute: UIView.toastViewCancelTask!)
}
}

Connecting UIButtons by drawing a line

I need to connect those buttons with a line but have issues as you can see on the screenshot. I need the last line to go vertical (green circles). Any suggestions?
here is the code:
#IBAction func drawButtons (sender: AnyObject) {
buttonContainerView.removeFromSuperview() // Clear containerView
buttonContainerView = UIView() // Create a new instance
let buttonCount = array.count
let n = Int(self.view.frame.size.width) / 90 //check how many buttons can fit in the screen
let buttonsPerRow = n
let horizontalSpace: CGFloat = 80
let verticalSpace: CGFloat = 80
// Create the alignment points
var points = [CGPointZero]
var direction: CGFloat = 1
for i in 1..<buttonCount {
let previousPoint = points[i-1]
let point: CGPoint
if i % buttonsPerRow == 0 {
direction *= -1
point = CGPointMake(previousPoint.x, previousPoint.y + verticalSpace)
} else {
point = CGPointMake(previousPoint.x + direction * horizontalSpace, previousPoint.y)
}
points.append(point)
}
// Make the buttons
var containerWidth: CGFloat = 0
var containerHeight: CGFloat = 0
for (index, point) in points.enumerate() {
let button = UIButton(frame: CGRectMake(point.x, point.y, 60, 60))
button.setTitle("Button \(index)", forState: .Normal)
button.setTitleColor(button.tintColor, forState: .Normal)
button.layer.cornerRadius = 30
button.layer.borderColor = UIColor .redColor().CGColor
button.layer.borderWidth = 1
buttonContainerView.addSubview(button)
// Determine size needed in the container to show all button
if button.frame.maxX > containerWidth {
containerWidth = button.frame.maxX
}
if button.frame.maxY > containerHeight {
containerHeight = button.frame.maxY
}
let myBezierPath = UIBezierPath()
myBezierPath.moveToPoint(CGPointMake(point.x + 60, point.y + 30))
myBezierPath.addLineToPoint(CGPointMake(point.x + 80, point.y + 30))
let shapeLayer = CAShapeLayer()
shapeLayer.path = myBezierPath .CGPath
shapeLayer.strokeColor = UIColor.blueColor().CGColor
shapeLayer.lineWidth = 2
shapeLayer.fillColor = UIColor.clearColor().CGColor
buttonContainerView.layer.addSublayer(shapeLayer)
}
// Optional: draw the alignment points and give the container view a background color
// so it's easier to visualize
for _ in points {
for (index, point) in points.enumerate() {
let circleLabel = UILabel(frame: CGRectMake(point.x, point.y, 11, 11))
circleLabel.layer.cornerRadius = 5.5
circleLabel.text = String(index + 1)
circleLabel.textAlignment = NSTextAlignment.Center
circleLabel.font = circleLabel.font.fontWithSize(8)
buttonContainerView.addSubview(circleLabel)
}
}
// buttonContainerView.backgroundColor = UIColor.lightGrayColor()
// Center the containerView on the screen
buttonContainerView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(buttonContainerView)
let c1 = NSLayoutConstraint(item: buttonContainerView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0)
let c2 = NSLayoutConstraint(item: buttonContainerView, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1, constant: 0)
let c3 = NSLayoutConstraint(item: buttonContainerView, attribute: .Width, relatedBy: .Equal , toItem: nil, attribute: .Width, multiplier: 0, constant: containerWidth)
let c4 = NSLayoutConstraint(item: buttonContainerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 0, constant: containerHeight)
NSLayoutConstraint.activateConstraints([c1, c2, c3, c4])
}
}
I have also tried this which seems to be working now, but I'm open to suggestions
for i in 1..<buttonCount {
let previousPoint = points[i-1]
let point: CGPoint
if i % buttonsPerRow == 0 {
direction *= -1
point = CGPointMake(previousPoint.x, previousPoint.y + verticalSpace)
let lineView = UIView (frame: CGRectMake(previousPoint.x + 30, previousPoint.y + lineVerticalSpace, 1, 20))
lineView.layer.borderColor = UIColor .blueColor().CGColor
lineView.layer.borderWidth = 3
buttonContainerView.addSubview(lineView)
} else {
point = CGPointMake(previousPoint.x + direction * horizontalSpace, previousPoint.y)
}
points.append(point)
// print(direction)
if direction == -1 {
let lineView = UIView (frame: CGRectMake(previousPoint.x + (direction * lineHorizontalSpace + 40), point.y + 30, 20, 1))
lineView.layer.borderColor = UIColor .redColor().CGColor
lineView.layer.borderWidth = 3
buttonContainerView.addSubview(lineView)
}
else {
let lineView = UIView (frame: CGRectMake(previousPoint.x + direction * lineHorizontalSpace, point.y + 30, 20, 1))
lineView.layer.borderColor = UIColor .redColor().CGColor
lineView.layer.borderWidth = 3
buttonContainerView.addSubview(lineView)
}
}
Which results in this:

Swift : How to make a Black screen to guidelines for user to use app

Im using swift 1.2.
I need to create a guideline for user to use my app.
it look like: a black transparent screen, 1 button highlight with note how to use, what is it.
i have searching for solution but still get stuck.
Can anybody help me?
Thanks!
If I understood correctly what you want, it can be done something like this*:
class TutorialView : UIView
{
override init(frame: CGRect)
{
super.init(frame: frame)
self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
}
convenience init()
{
self.init(frame: CGRectZero)
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
func showFromWindow()
{
let window = UIApplication.sharedApplication().keyWindow!
self.translatesAutoresizingMaskIntoConstraints = false
window.addSubview(self)
window.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[tutorialView]-0-|", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: nil, views: ["tutorialView": self]))
window.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[tutorialView]-0-|", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: nil, views: ["tutorialView": self]))
}
func addCommentToTopRightItem(comment: String, afterDelay: NSTimeInterval)
{
let topMargin : Int32 = 54
let rightMargin : Int32 = 24
let height : Int32 = 100
// we need two views - one for arrow and second for text
let commentLabel = UILabel()
let arrowView = UIView()
commentLabel.translatesAutoresizingMaskIntoConstraints = false
arrowView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(commentLabel)
self.addSubview(arrowView)
// setup layout for views
// |^^^^^|
// |^^^^^|arrow|
// |label|_____|
// |_____|
//
// we place center of Y label to the bottom of arrow view
// to an arrow coming out of the middle of text
let metrics = ["topmargin" : NSNumber(int: topMargin), "rightmargin": NSNumber(int: rightMargin),"height" : NSNumber(int: height)]
let views = ["label": commentLabel, "arrow": arrowView]
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-topmargin-[arrow(==height)]", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: metrics, views: views))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[label(==height)]", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: metrics, views: views))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-rightmargin-[arrow(==height)]-[label]-0-|", options: NSLayoutFormatOptions.DirectionRightToLeft, metrics: metrics, views: views))
self.addConstraint(NSLayoutConstraint(item: commentLabel, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: arrowView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 1.0))
// configure text label
commentLabel.textColor = UIColor.whiteColor()
commentLabel.numberOfLines = 0
commentLabel.textAlignment = NSTextAlignment.Right
commentLabel.font = UIFont(name: "HelveticaNeue-Light", size: 16)
commentLabel.text = comment
// configure arrow view
self.layoutIfNeeded()
let d : CGFloat = min(arrowView.frame.size.height, arrowView.frame.size.width)
// draw arc line from {0, 1} to {1, 0}
// | /|
// |/_|
let linePath = UIBezierPath()
linePath.moveToPoint(CGPointMake(0, d))
linePath.addCurveToPoint(CGPointMake(d, 0), controlPoint1: CGPointMake(d*0.5, d), controlPoint2: CGPointMake(d, d*0.5))
let lineLayer = CAShapeLayer()
lineLayer.path = linePath.CGPath
lineLayer.strokeColor = UIColor.redColor().CGColor
lineLayer.backgroundColor = UIColor.clearColor().CGColor
lineLayer.fillColor = UIColor.clearColor().CGColor
lineLayer.lineWidth = 2
// draw triangle near {1, 0} point
let trianglePath = UIBezierPath()
trianglePath.moveToPoint(CGPointMake(d, 0))
trianglePath.addLineToPoint(CGPointMake(d - 5, 15))
trianglePath.addLineToPoint(CGPointMake(d + 5, 15))
trianglePath.closePath()
let triangleLayer = CAShapeLayer()
triangleLayer.path = trianglePath.CGPath
triangleLayer.strokeColor = UIColor.redColor().CGColor
triangleLayer.backgroundColor = UIColor.clearColor().CGColor
triangleLayer.fillColor = UIColor.redColor().CGColor
triangleLayer.lineWidth = 2
// line + triangle = arrow :)
arrowView.layer.addSublayer(lineLayer)
arrowView.layer.addSublayer(triangleLayer)
arrowView.alpha = 0.0
commentLabel.alpha = 0.0
UIView.animateWithDuration(0.8, delay: afterDelay, options: UIViewAnimationOptions.AllowAnimatedContent, animations:
{ () -> Void in
arrowView.alpha = 1.0
commentLabel.alpha = 1.0
}, completion: nil)
}
func addButton(title: String, highlighteAfterDelay: NSTimeInterval)
{
// same as before
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(button)
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 1.0))
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: -20.0))
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 120.0))
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 40.0))
button.setTitle(title, forState: UIControlState.Normal)
button.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
button.layer.cornerRadius = 20.0
button.layer.masksToBounds = false
button.layer.borderWidth = 1.0
button.layer.borderColor = UIColor.whiteColor().CGColor
button.layer.shadowColor = UIColor.whiteColor().CGColor
button.layer.shadowOpacity = 0.4
button.layer.shadowRadius = 4
button.layer.shadowOffset = CGSizeMake(0, 0)
button.alpha = 0.2
button.enabled = false
UIView.animateWithDuration(0.8, delay: highlighteAfterDelay, options: UIViewAnimationOptions.AllowAnimatedContent, animations:
{ () -> Void in
button.alpha = 1.0
button.enabled = true
}, completion: nil)
}
}
In some view controller:
let tutorial = TutorialView()
tutorial.showFromWindow()
tutorial.addCommentToTopRightItem("Tap this button to do something amazing action!", afterDelay: 1.0)
tutorial.addButton("Done", highlighteAfterDelay: 3.0)
Result:
*I don't have a compiler version 1.2 but I think that there should be no serious differences.
I see the problem now, you can add UView on top of it, give it a color with alpha, and get frame by using
convertRect(button.frame, toView:overlayView)
Hopefuly helps

Resources