So, I'm following the iOS-Charts tutorial on AppCoda, I followed it perfectly, then when I was having issues I also copied the code to match their exactly.
The problem is, when I try and create a pie chart, then run it. I get the error:
Thread1: EXC_BAD_ACCESS (code=2, address=0x2a0c220)
and the app crashes. When it crashes, it highlights this line:
pieChartView.data = pieChartData
Here is as screen shot of the screen:
Here is the full code from the viewcontroller.swift:
import UIKit
import Charts
class ViewController: UIViewController {
#IBOutlet weak var pieChartView: PieChartView!
func setChart(dataPoints: [String], values: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = ChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let pieChartDataSet = PieChartDataSet(yVals: dataEntries, label: "Units Sold")
let pieChartData = PieChartData(xVals: dataPoints, dataSet: pieChartDataSet)
pieChartView.data = pieChartData
var colors: [UIColor] = []
for i in 0..<dataPoints.count {
let red = Double(arc4random_uniform(256))
let green = Double(arc4random_uniform(256))
let blue = Double(arc4random_uniform(256))
let color = UIColor(red: CGFloat(red/255), green: CGFloat(green/255), blue: CGFloat(blue/255), alpha: 1)
colors.append(color)
}
pieChartDataSet.colors = colors
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0]
setChart(months, values: unitsSold)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
It looks you might have missed this part:
Next drag a View onto the Bar Chart View Controller and pin its edges as shown. This View is a child of the main View in the controller.
You need to create a UIView in your View Controller and link this to the #IBOutlet weak var pieChartView: PieChartView! in your code.
Then it will work.
And it looks like a nice library.
Hope it helps. Please let us know.
EDIT:
Go to your View Controller on the storyboard and drag a UIView. Then select UIView and set its class as PieChartView in identity inspector. Open the ViewController swift file and right click on the little circle on the #IBOutlet line at the top and drag it on to the UIView that you just created by holding the right click and leave it. Now you connected outlet to the code and it will recognize the pieChartView var as an object instead nil which currently is.
I had exactly the same error with the same code.
Like smozgur suggest, i checked for Iboutlet connection but it seemed to be linked fine
The issue was that i had set the wrong module, so check for the "module target" too
I hope this can be helpful for someone :)
Related
I have a fairly simple code which, upon clicking a button, adds a randomly colored UIView to a UIStackView, and upon a different button click, removes a random UIView from the UIStackView.
Here's the code:
import UIKit
class ViewController: UIViewController, Storyboarded {
weak var coordinator: MainCoordinator?
#IBOutlet weak var stackView: UIStackView!
var tags: [Int] = []
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPressed(_ sender: UIButton) {
switch sender.tag {
case 10:
let view = UIView(frame: CGRect(x: 0, y: 0, width: stackView.frame.width, height: 20))
var number = Int.random(in: 0...10000)
while tags.contains(number) {
number = Int.random(in: 0...10000)
}
tags.append(number)
view.tag = number
view.backgroundColor = .random()
stackView.addArrangedSubview(view)
case 20:
if tags.count == 0 {
print("Empty")
return
}
let index = Int.random(in: 0...tags.count - 1)
let tag = tags[index]
tags.remove(at: index)
if let view = stackView.arrangedSubviews.first(where: { $0.tag == tag }) {
stackView.removeArrangedSubview(view)
}
default:
break
}
}
}
extension CGFloat {
static func random() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UInt32.max)
}
}
extension UIColor {
static func random() -> UIColor {
return UIColor(
red: .random(),
green: .random(),
blue: .random(),
alpha: 1.0
)
}
}
I'm not using removeFromSuperview on purpose - since I would (later) want to reuse those removed UIViews, and that is why I'm using removeArrangedSubview.
The issue I'm facing is:
All UIViews are removed as expected (visually of course, I know they're still in the memory) until I reach the last one - which, even though was removed, still appears and filling the entire UIStackView.
What am I missing here?
You can understand removeArrangedSubview is for removing constraints that were assigned to the subview. Subviews are still in memory and also still inside the parent view.
To achieve your purpose, you can define an array as your view controller's property, to hold those subviews, then use removeFromSuperview.
Or use .isHidden property on any subview you need to keep it in memory rather than removing its contraints. You will see the stackview do magical things to all of its subviews.
let subview = UIView()
stackView.addArrangedSubview(subview)
func didTapButton(sender: UIButton) {
subview.isHidden.toggle()
}
Last, addArrangedSubview will do two things: add the view to superview if it's not in superview's hierachy and add contraints for it.
I'm trying to add a search bar to a navigation map for MapBox on iOS, but having some trouble. I'm trying to do it programmatically in this case, as I don't have a great handle on storyboard. I've tried messing around with the zIndex as well, but still no search bar shows up for me.
Here's the code I've got so far:
class ViewController: UIViewController, UISearchBarDelegate, MGLMapViewDelegate {
var mapView: NavigationMapView!
lazy var directions: DirectionsManager = DirectionsManager()
var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
// Setup mapbox
let styleURL = URL(string: "style")
mapView = NavigationMapView(frame: view.bounds, styleURL: styleURL)
view.addSubview(mapView)
mapView.delegate = self
mapView.showsUserLocation = true
mapView.setUserTrackingMode(.follow, animated: true)
mapView.localizeLabels()
self.searchBar = UISearchBar()
mapView.addSubview(searchBar)
}
#erikpartridge, It's not an issue with mapbox, you need to set frame for UISearchBar like the below,
self.searchBar = UISearchBar(frame: CGRect(x: 15, y: 50, width: (view.bounds.width-30), height: 50))
mapView.addSubview(searchBar)
This is probably very simple, but I am not used to Iphone programming and Swift yet. I have only worked with Android. I want to use iosChart in my application. I have followed this tutorial to create a test bar chart.
The problem is that the chart does not fit the screen, but extends outside the display in the emulator. There is probably something easy to fix in the storyboard, but I have not found anything.
Here is how it looks like:
The x-axis should extend to December, but it is not visible.
Here is the code:
import UIKit
import Charts
class DetailedScoreController: UIViewController {
var results : Results
var months: [String]!
// MARK: Initialization
#IBOutlet weak var detailedChart: BarChartView!
required init?(coder aDecoder: NSCoder) {
self.results = Results()!
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
print("DetailedResultController Created!");
self.results.printResult()
self.results.printDetailedResults()
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0, 4.0, 18.0, 2.0, 4.0, 5.0, 4.0]
setChart(months, values: unitsSold)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Set up barchart
func setChart(dataPoints: [String], values: [Double]) {
var dataEntries: [BarChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = BarChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(yVals: dataEntries, label: "Units Sold")
let chartData = BarChartData(xVals: months, dataSet: chartDataSet)
detailedChart.data = chartData
}
}
Here is how the storyboard looks like, the marked is the bar chart:
To me it looks like the entire chart should lie in the display.
Click on your view and select the pin at the bottom right hand corner to add constraints.
Set the top, bottom, left and right to 10 and click 'Add constraints'. It should now show the entire bar chart as expected.
Add constraints to your view in storyboard to make it change size to fit the screen:
About Auto Layout and Layout Constraints
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