I wan't to draw tags on the screen and put them from left to the right and in the case where there are too many for 1 line go to the newline. Like this :
Currently I'm stuck I don't know how to get the width of label after it's compute by constraints. I need to find tagWidth
enum TagPosition {
case left_top
case left
case top
case other
}
func createProductTags() {
var lineWidth: CGFloat = 8.0
var line = 0
var lastTag: UIView = self.contentView
var position = TagPosition.other
var viewAbove = self.contentView
for i in 0..<menu.tagName.count {
if (line == 0 && lineWidth == 8.0) {
position = TagPosition.left_top
} else if (line == 0) {
position = TagPosition.top
} else if (lineWidth == 8.0) {
position = TagPosition.left
} else {
position = TagPosition.other
}
self.tag = setTagSettings(named: menu.tagName[i], position: position)
var tagConstraints = setTagConstraints(lastTag: lastTag, viewAbove: viewAbove, position: position)
lineWidth += tagWidth + 8.0
if (lineWidth > self.contentView.bounds.width - 16) {
viewAbove = lastTag
updateConstraintWhenNewLine(tagConstraints: &tagConstraints, viewAbove: viewAbove)
line += 1
lineWidth = tagWidth + 16.0
}
lastTag = self.tag
}
// To fit the contentView to the last line of tags
lastTag.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
}
func updateConstraintWhenNewLine(tagConstraints: inout [NSLayoutConstraint], viewAbove: UIView) {
// Change constraints of last label of the line if it doesn't fit on the line
//Remove old contraints
topConstraint.isActive = false
leftConstraint.isActive = false
tagConstraints.remove(object: topConstraint)
tagConstraints.remove(object: leftConstraint)
self.tag.removeConstraints([topConstraint, leftConstraint])
//Add old contraints
topConstraint = self.tag.topAnchor.constraint(equalTo: viewAbove.bottomAnchor, constant: 8)
leftConstraint = self.tag.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 16)
tagConstraints.append(contentsOf: [topConstraint, leftConstraint])
NSLayoutConstraint.activate([topConstraint, leftConstraint])
}
func setTagSettings(named tagName: String, position: TagPosition) -> UIView {
// tagContainer settings
let tagContainer = UIView()
tagContainer.backgroundColor = DiscoderyAppSettings.sharedInstance.primaryColor
tagContainer.layer.masksToBounds = true
tagContainer.layer.cornerRadius = self.tagHeight / 2
tagContainer.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(tagContainer)
// tagLabel settings
let tagLabel = UILabel()
tagLabel.text = tagName
tagLabel.font?.withSize(16.0)
tagLabel.textColor = UIColor.white
tagLabel.textAlignment = .center
tagLabel.translatesAutoresizingMaskIntoConstraints = false
tagContainer.addSubview(tagLabel)
// tagLabel constraints
tagLabel.topAnchor.constraint(equalTo: tagContainer.topAnchor).isActive = true
tagLabel.leftAnchor.constraint(equalTo: tagContainer.leftAnchor, constant: 8).isActive = true
tagLabel.rightAnchor.constraint(equalTo: tagContainer.rightAnchor, constant: -8).isActive = true
tagLabel.bottomAnchor.constraint(equalTo: tagContainer.bottomAnchor).isActive = true
return tagContainer
}
func setTagConstraints(lastTag: UIView, viewAbove: UIView, position: TagPosition) -> [NSLayoutConstraint] {
var tagConstraints: [NSLayoutConstraint] = [NSLayoutConstraint]()
heightConstraint = self.tag.heightAnchor.constraint(equalToConstant: tagHeight)
switch position {
case .left_top:
topConstraint = self.tag.topAnchor.constraint(equalTo: self.contentView.topAnchor)
leftConstraint = self.tag.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 16)
case .left:
topConstraint = self.tag.topAnchor.constraint(equalTo: viewAbove.bottomAnchor, constant: 8)
leftConstraint = self.tag.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 16)
case .top:
topConstraint = self.tag.topAnchor.constraint(equalTo: self.contentView.topAnchor)
leftConstraint = self.tag.leftAnchor.constraint(equalTo: lastTag.rightAnchor, constant: 8)
default:
topConstraint = self.tag.topAnchor.constraint(equalTo: viewAbove.bottomAnchor, constant: 8)
leftConstraint = self.tag.leftAnchor.constraint(equalTo: lastTag.rightAnchor, constant: 8)
}
tagConstraints.append(contentsOf: [heightConstraint, topConstraint, leftConstraint])
NSLayoutConstraint.activate(tagConstraints)
return constraints
}
createProductTags() is called in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
If you need any other detail ask me. Thank you
Related
I am using the swift charts library found here. When I call our api, we have anywhere from 1 data point up to 24 data points for 24 hrs in a day. When I have a lot of data our width of bars are great. When I have 1 or or up to 5 the widths are very wide and dont look good. I assume its because its trying to fill up the space. does anyone know how I could adjust it to be skinny and not take up the space.
In update view I tried to do various different zooms based on the amount of data but it seems as if there is a min zoom or something in the library.
//code below
import Charts
import SwiftUI
struct TransactionBarChartView: UIViewRepresentable {
let entries: [BarChartDataEntry]
let barChart = BarChartView()
#Binding var selectedYear: Int
#Binding var selectedItem: String
func makeUIView(context: Context) -> BarChartView {
barChart.delegate = context.coordinator
return barChart
}
func updateUIView(_ uiView: BarChartView, context: Context) {
let dataSet = BarChartDataSet(entries: entries)
dataSet.label = "Transactions"
uiView.noDataText = "No Data"
uiView.data = BarChartData(dataSet: dataSet)
uiView.rightAxis.enabled = false
/*if uiView.scaleX == 1.0 {
uiView.zoom(scaleX: 1.5, scaleY: 1, x: 0, y: 0)
}*/
if entries.count < 8 {
uiView.zoom(scaleX: 0.2, scaleY: 1, x: 0, y: 0)
}
if entries.count < 4 {
uiView.zoom(scaleX: 0.2, scaleY: 1, x: 0, y: 0)
}
if entries.count < 2 {
uiView.zoom(scaleX: 0.0005, scaleY: 1, x: 0.05, y: 0)
}
uiView.setScaleEnabled(false)
formatDataSet(dataSet: dataSet)
formatLeftAxis(leftAxis: uiView.leftAxis)
formatXAxis(xAxis: uiView.xAxis)
formatLegend(legend: uiView.legend)
uiView.notifyDataSetChanged()
}
class Coordinator: NSObject, ChartViewDelegate {
let parent:TransactionBarChartView
init(parent: TransactionBarChartView) {
self.parent = parent
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
let month = WineTransaction.months[Int(entry.x)]
let quantity = Int(entry.y)
parent.selectedItem = "\(quantity) sold in \(month)"
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func formatDataSet(dataSet: BarChartDataSet) {
dataSet.colors = [.red]
dataSet.valueColors = [.red]
let formatter = NumberFormatter()
formatter.numberStyle = .none
dataSet.valueFormatter = DefaultValueFormatter(formatter: formatter)
}
func formatLeftAxis(leftAxis: YAxis) {
leftAxis.labelTextColor = .red
let formatter = NumberFormatter()
formatter.numberStyle = .none
leftAxis.valueFormatter = DefaultAxisValueFormatter(formatter: formatter)
leftAxis.axisMinimum = 0
}
func formatXAxis(xAxis: XAxis) {
xAxis.valueFormatter = IndexAxisValueFormatter(values: WineTransaction.months)
xAxis.labelPosition = .bottom
xAxis.granularityEnabled = true
xAxis.labelTextColor = .red
}
func formatLegend(legend: Legend) {
legend.textColor = .red
legend.horizontalAlignment = .right
legend.verticalAlignment = .top
legend.drawInside = true
legend.yOffset = 30.0
}
}
I am using Charts library (version 3.5.0). Usually the chart is drawn without issue but randomly the app crashes with the following XCode Crash stack screenshot and Crashlytics stack trace screenshot
Class with chart view
class ChartContainerView: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var chartView: CustomLineChartView!
var chartData: LineChartDataSet?
struct TickData: Codable {
let price: Double
let time: Int
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("ChartContainerView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
chartView.resetChart()
}
func drawChart(withData tickArray: [TickData]) {
chartView.resetChart()
var entries = [ChartDataEntry]()
for (i, tick) in tickArray.enumerated() {
entries.append(ChartDataEntry(x: Double(i), y: tick.price))
}
chartData = LineChartDataSet(entries: entries)
let xValueCount = Double(chartData!.count)
chartView.xAxis.axisMaximum = xValueCount
chartView.setVisibleXRangeMaximum(60.0 * 5)
chartView.setVisibleXRangeMinimum(60.0 * 5)
chartView.setVisibleXRange(minXRange: 5, maxXRange: 2000)
chartView.data = createLineData(lineDataSet: chartData)
chartView.moveViewToX(xValueCount)
chartView.animate(xAxisDuration: 0.5)
}
func createLineData(lineDataSet: LineChartDataSet) -> LineChartData {
lineDataSet.mode = .cubicBezier
lineDataSet.drawCirclesEnabled = true
lineDataSet.circleRadius = 2
lineDataSet.drawCircleHoleEnabled = false
lineDataSet.axisDependency = .right
lineDataSet.lineWidth = 1
lineDataSet.fillAlpha = 0
lineDataSet.highlightLineWidth = 0.3
lineDataSet.drawValuesEnabled = false
lineDataSet.drawFilledEnabled = false
return LineChartData(dataSet: lineDataSet)
}
//This method is called on a background thread
func updateChart(withData tickArray: [TickData]) {
let count = chartData!.count
for (i, tick) in tickArray.enumerated() {
let entry = ChartDataEntry(x: Double(count + i), y: tick.price))
chartData?.append(entry)
}
DispatchQueue.main.async(execute: {
chartView.data?.notifyDataChanged()
chartView.notifyDataSetChanged()
})
}
}
Custom LineChartView
class CustomLineChartView: LineChartView {
override func initialize() {
super.initialize()
renderer = CustomLineChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler)
chartDescription?.enabled = false
dragYEnabled = false
scaleYEnabled = false
pinchZoomEnabled = false
doubleTapToZoomEnabled = false
drawGridBackgroundEnabled = false
dragDecelerationFrictionCoef = 0.0
highlightPerTapEnabled = false
autoScaleMinMaxEnabled = true
highlightPerDragEnabled = false
noDataText = ""
leftAxis.enabled = false
legend.enabled = false
dragXEnabled = true
scaleXEnabled = false
xAxis.labelCount = 5
xAxis.labelPosition = .bottom
xAxis.drawAxisLineEnabled = false
xAxis.drawGridLinesEnabled = false
xAxis.granularityEnabled = true
xAxis.granularity = 1.0
rightAxis.labelCount = 5
rightAxis.labelPosition = .outsideChart
rightAxis.drawAxisLineEnabled = false
rightAxis.drawGridLinesEnabled = false
rightAxis.granularityEnabled = true
rightAxis.centerAxisLabelsEnabled = true
rightAxis.granularity = 1.0
rightAxis.drawLimitLinesBehindDataEnabled = false
}
func resetChart() {
clearAllViewportJobs()
removeAnimation()
clearValues()
clear()
data = nil
rightAxis.customYMax = 0
rightAxis.customYMin = 0
zoom(scaleX: 0, scaleY: 0, x: 0, y: 0)
}
}
As this is random, I am unable to find why the entries object's address is corrupted in ChartDataSet.swift
I am trying to recreate the iPhone App Switcher page - the one that comes up when you swipe up.
I'm building it by adding an array of views representing apps to a scroll view.
Unfortunately, I am getting caught up setting the spacing between the views. I am trying to set it using a parabolic function so the views collapse to the left hand side. I think the equation might be incorrect.
Here's my code for scrollViewDidScroll:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
items.enumerated().forEach { (index, tabView) in
guard let tabSuperView = tabView.superview else {return}
let screenWidth = UIScreen.main.bounds.width
// Return value between 0 and 1 depending on the location of the tab within the visible screen
// 0 Left hand side or offscreen
// 1 Right hand side or offscreen
let distanceMoved = tabSuperView.convert(CGPoint(x: tabView.frame.minX, y: 0), to: view).x
let screenOffsetPercentage: CGFloat = distanceMoved / screenWidth
// Scale
let minValue: CGFloat = 0.6
let maxValue: CGFloat = 1
let scaleAmount = minValue + (maxValue - minValue) * screenOffsetPercentage
let scaleSize = CGAffineTransform(scaleX: scaleAmount, y: scaleAmount)
tabView.transform = scaleSize
// Set a max and min
let percentAcrossScreen = max(min(distanceMoved / screenWidth, 1.0), 0)
// Spacing
if let prevTabView = items.itemAt(index - 1) {
// Rest of tabs
let constant: CGFloat = 100
let xFrame = prevTabView.frame.origin.x + (pow(percentAcrossScreen, 2) * constant)
tabView.frame.origin.x = max(xFrame, 0)
} else {
// First tab
tabView.frame.origin.x = 20
}
}
}
How would you fix this to replicate the scrolling experience of the iPhone app switcher page?
Sample Project:
https://github.com/Alexander-Frost/ViewContentOffset
The general idea (I'll call the moving views "cards")...
As you "push" a card to the right, calculate the percentage of the distance from the leading edge of the container to the leading edge of the card, based on a portion of the container width. Then, position the leading edge of the next card that percentage of the width of the card.
So, if the cards are 70% of the width of the view, we want the top card to be almost pushed off to the right when the "dragging" card is 1/3rd of the distance from the leading edge of the view.
If the dragging card is one-half of 1/3rd, we want the next card's leading to be 1/2 the width of the card.
As I said in one of your earlier questions, I'm not sure using a scroll view will be of benefit, since you'll be changing the relative distances as you drag.
Here's an example:
You can try out this code - just create a new project and replace the default view controller class with:
class ViewController: UIViewController {
let switcherView = SwitcherView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
switcherView.translatesAutoresizingMaskIntoConstraints = false
switcherView.backgroundColor = .white
view.addSubview(switcherView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain switcher view to all 4 sides of safe area
switcherView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
switcherView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
switcherView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
switcherView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
}
}
Here's the "card" view class:
class CardView: UIView {
var theLabels: [UILabel] = []
var cardID: Int = 0 {
didSet {
theLabels.forEach {
$0.text = "\(cardID)"
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
for i in 1...5 {
let v = UILabel()
v.font = .systemFont(ofSize: 24.0)
v.translatesAutoresizingMaskIntoConstraints = false
addSubview(v)
switch i {
case 1:
v.topAnchor.constraint(equalTo: topAnchor, constant: 10.0).isActive = true
v.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16.0).isActive = true
case 2:
v.topAnchor.constraint(equalTo: topAnchor, constant: 10.0).isActive = true
v.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16.0).isActive = true
case 3:
v.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10.0).isActive = true
v.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16.0).isActive = true
case 4:
v.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10.0).isActive = true
v.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16.0).isActive = true
default:
v.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
v.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
theLabels.append(v)
}
layer.cornerRadius = 6
// border
layer.borderWidth = 1.0
layer.borderColor = UIColor.gray.cgColor
// shadow
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: -3, height: 3)
layer.shadowOpacity = 0.25
layer.shadowRadius = 2.0
}
}
and here's the "SwitcherView" class - where all the action takes place:
class SwitcherView: UIView {
var cards: [CardView] = []
var currentCard: CardView?
var firstLayout: Bool = true
// useful during development...
// if true, highlight the current "control" card in yellow
// if false, leave them all cyan
let showHighlight: Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
clipsToBounds = true
// add 20 "cards" to the view
for i in 1...20 {
let v = CardView()
v.backgroundColor = .cyan
v.cardID = i
cards.append(v)
addSubview(v)
v.isHidden = true
}
// add a pan gesture recognizer to the view
let pan = UIPanGestureRecognizer(target: self, action: #selector(self.didPan(_:)))
addGestureRecognizer(pan)
}
override func layoutSubviews() {
super.layoutSubviews()
if firstLayout {
// if it's the first time through, layout the cards
firstLayout = false
if let firstCard = cards.first {
if firstCard.frame.width == 0 {
cards.forEach { thisCard in
//thisCard.alpha = 0.750
thisCard.frame = CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: self.bounds.height))
thisCard.transform = CGAffineTransform(scaleX: 0.71, y: 0.71)
thisCard.frame.origin.x = 0
if thisCard == cards.last {
thisCard.frame.origin.x = 10
}
thisCard.isHidden = false
}
doCentering(for: cards.last!)
}
}
}
}
#objc func didPan(_ gesture: UIPanGestureRecognizer) -> Void {
let translation = gesture.translation(in: self)
var pt = gesture.location(in: self)
pt.y = self.bounds.midY
for c in cards.reversed() {
if c.frame.contains(pt) {
if let cc = currentCard {
if let idx1 = cards.firstIndex(of: cc),
let idx2 = cards.firstIndex(of: c),
idx2 > idx1 {
if showHighlight {
currentCard?.backgroundColor = .cyan
}
currentCard = c
if showHighlight {
currentCard?.backgroundColor = .yellow
}
}
} else {
currentCard = c
if showHighlight {
currentCard?.backgroundColor = .yellow
}
}
break
}
}
switch gesture.state {
case .changed:
if let controlCard = currentCard {
// update card leading edge
controlCard.frame.origin.x += translation.x
// don't allow drag left past 1.0
controlCard.frame.origin.x = max(controlCard.frame.origin.x, 1.0)
// update the positions for the rest of the cards
updateCards(controlCard)
gesture.setTranslation(.zero, in: self)
}
case .ended:
if showHighlight {
currentCard?.backgroundColor = .cyan
}
guard let controlCard = currentCard else {
return
}
if let idx = cards.firstIndex(of: controlCard) {
// use pan velocity to "throw" the cards
let velocity = gesture.velocity(in: self)
// convert to a reasonable Int value
let offset: Int = Int(floor(velocity.x / 500.0))
// step up or down in array of cards based on velocity
let newIDX = max(min(idx - offset, cards.count - 1), 0)
doCentering(for: cards[newIDX])
}
currentCard = nil
default:
break
}
}
func updateCards(_ controlCard: CardView) -> Void {
guard let idx = cards.firstIndex(of: controlCard) else {
print("controlCard not found in array of cards - can't update")
return
}
var relativeCard: CardView = controlCard
var n = idx
// for each card to the right of the control card
while n < cards.count - 1 {
let nextCard = cards[n + 1]
// get percent distance of leading edge of relative card
// to 33% of the view width
let pct = relativeCard.frame.origin.x / (self.bounds.width * 1.0 / 3.0)
// move next card that percentage of the width of a card
nextCard.frame.origin.x = relativeCard.frame.origin.x + (relativeCard.frame.size.width * pct) // min(pct, 1.0))
relativeCard = nextCard
n += 1
}
// reset relative card and index
relativeCard = controlCard
n = idx
// for each card to the left of the control card
while n > 0 {
let prevCard = cards[n - 1]
// get percent distance of leading edge of relative card
// to half the view width
let pct = relativeCard.frame.origin.x / self.bounds.width
// move prev card that percentage of 33% of the view width
prevCard.frame.origin.x = (self.bounds.width * 1.0 / 3.0) * pct
relativeCard = prevCard
n -= 1
}
self.cards.forEach { c in
let x = c.frame.origin.x
// scale transform each card between 71% and 75%
// based on card's leading edge distance to one-half the view width
let pct = x / (self.bounds.width * 0.5)
let sc = 0.71 + (0.04 * min(pct, 1.0))
c.transform = CGAffineTransform(scaleX: sc, y: sc)
// set translucent for far left cards
if cards.count > 1 {
c.alpha = min(1.0, x / 10.0)
}
}
}
func doCentering(for cCard: CardView) -> Void {
guard let idx = cards.firstIndex(of: cCard) else {
return
}
var controlCard = cCard
// if the leading edge is greater than 1/2 the view width,
// and it's not the Bottom card,
// set cur card to the previous card
if idx > 0 && controlCard.frame.origin.x > self.bounds.width * 0.5 {
controlCard = cards[idx - 1]
}
// center of control card will be offset to the right of center
var newX = self.bounds.width * 0.6
if controlCard == cards.last {
// if it's the Top card, center it
newX = self.bounds.width * 0.5
}
if controlCard == cards.first {
// if it's the Bottom card, center it + just a little to the right
newX = self.bounds.width * 0.51
}
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.1, options: [.allowUserInteraction, .curveEaseOut], animations: {
controlCard.center.x = newX
self.updateCards(controlCard)
}, completion: nil)
}
}
When we stop panning, the code finds the card with the Leading edge closest to the center of the view... If its leading edge is less-than 50% of the width, it slides it back to the left so it becomes the "center" card. If its leading edge is greater-than 50% of the width, it slides to the right and the previous card becomes the "center" card.
Edit - added some "Pan Velocity" handling to the SwitcherView.
I'm new to macos. I am developing an app for macos in which I want user to have side panels and he can hide/unhide as he desires.
I have used split view to split my screen into 3 parts. Right and left panels have proportional width to the view (not split view).
What I want is when the user hides the left panel, left pane should hide itself and the left anchor of window should move to right and the right anchor should stay at its position.
Similarly, if user hides right panel, the right panel should be hidden. Right anchor of window should move to left and left anchor should stay at its position.
the following function is called when the user wants to hide/unhide the left panel.
guard let window = self.view.window else {
return
}
var frame = window.frame
if window.isZoomed {
if leftPane.contentView!.isHidden {
self.leftPane.isHidden = false
self.leftPane.contentView?.isHidden = false
} else {
self.leftPane.contentView?.isHidden = true
}
} else {
if leftPaneHidden {
self.leftPane.isHidden = false
frame = NSRect(x: (frame.origin.x - leftPane.frame.size.width), y: frame.origin.y, width: (frame.size.width + leftPane.frame.size.width), height: frame.size.height)
leftPaneHidden = false
} else {
self.leftPane.isHidden = true
frame = NSRect(x: (frame.origin.x + leftPane.frame.size.width), y: frame.origin.y, width: (frame.size.width - leftPane.frame.size.width), height: frame.size.height)
leftPaneHidden = true
}
self.view.window?.setFrame(frame, display: true, animate: true)
I want left panel to be hidden as it works in notability app.
If you did mean that you want the window to shrink/expand, here's a working demo. If you want it to work the way Xcode works, then delete the messing around with the frame. It's implemented programmatically so I don't have to describe the storyboard, but you can easily do the same thing with storyboards. The only code in the left & right views sets the background color so you can see that it works.
SplitViewController:
class MainSplitViewController: NSSplitViewController {
weak var leftItem: NSSplitViewItem?
weak var rightItem: NSSplitViewItem?
convenience init(identifier: NSUserInterfaceItemIdentifier) {
self.init()
splitView.identifier = identifier
splitView.wantsLayer = true
splitView.layer?.backgroundColor = NSColor.darkGray.cgColor
splitView.dividerStyle = .thin
let vcL = SubViewController(NSView(), backgroundColor: .red)
let vcM = MainViewController(MainView(self), backgroundColor: .green)
let vcR = SubViewController(NSView(), backgroundColor: .blue)
vcL.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true
vcM.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true
vcR.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true
let sidebarItem = NSSplitViewItem(viewController: vcL)
sidebarItem.canCollapse = true
sidebarItem.holdingPriority = NSLayoutConstraint.Priority(NSLayoutConstraint.Priority.defaultLow.rawValue + 1)
addSplitViewItem(sidebarItem)
leftItem = sidebarItem
let mainItem = NSSplitViewItem(viewController: vcM)
addSplitViewItem(mainItem)
let inspItem = NSSplitViewItem(viewController: vcR)
inspItem.canCollapse = true
inspItem.holdingPriority = NSLayoutConstraint.Priority(NSLayoutConstraint.Priority.defaultLow.rawValue + 1)
addSplitViewItem(inspItem)
rightItem = inspItem
}
}
Middle view, with buttons to toggle the side views:
class MainView: NSView, DebugHelper {
weak var splitViewController: MainSplitViewController?
func labeledButton(_ stringValue: String = "") -> NSButton {
let button = NSButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 28.0)
button.bezelStyle = NSButton.BezelStyle.rounded
button.title = stringValue
return button
}
private func adjustWindowFrame(_ item: NSSplitViewItem, left: Bool) {
let view = item.viewController.view
let frame = window!.frame
if item.isCollapsed {
let minX = left ? frame.minX - view.frame.width : frame.minX
let newFrame = NSRect(x: minX, y: frame.minY, width: frame.width + view.frame.width, height: frame.height)
window!.setFrame(newFrame, display: true)
} else {
let minX = left ? frame.minX + view.frame.width : frame.minX
let newFrame = NSRect(x: minX, y: frame.minY, width: frame.width - view.frame.width, height: frame.height)
window!.setFrame(newFrame, display: true)
}
item.isCollapsed = !item.isCollapsed
}
#objc func collapseLeft(_ sender: Any) {
guard let item = splitViewController?.leftItem else { return }
adjustWindowFrame(item, left: true)
}
#objc func collapseRight(_ sender: Any) {
guard let item = splitViewController?.rightItem else { return }
adjustWindowFrame(item, left: false)
}
convenience init(_ parent: MainSplitViewController) {
self.init(frame: .zero)
splitViewController = parent
translatesAutoresizingMaskIntoConstraints = false
let lButton = labeledButton("Collapse Left")
addSubview(lButton)
lButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20).isActive = true
lButton.centerXAnchor.constraint(equalTo: centerXAnchor, constant: -100).isActive = true
lButton.action = #selector(collapseLeft)
lButton.target = self
let rButton = labeledButton("Collapse Right")
addSubview(rButton)
rButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20).isActive = true
rButton.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 100).isActive = true
rButton.action = #selector(collapseRight)
rButton.target = self
}
}
class MainViewController: NSViewController {
convenience init(_ view: NSView, backgroundColor: NSColor = .white) {
self.init()
self.view = view
self.view.wantsLayer = true
self.view.layer?.backgroundColor = backgroundColor.cgColor
}
}
I am trying to setup a line chart with one fill colour but for some reason, the fill colour is faded.
Example
Both the random view I have added to middle of screen and the fill colour of the line chart are set to be red, but for some reason the fill colour of the chart is faded.
Can see code here
#IBOutlet var liveChart : LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Chart Tests"
configureChart(chart: liveChart)
var xAxis = [String]()
var yAxis = [Double]()
for _ in 0..<10
{
xAxis.append("")
let yVal = Double(randomBetweenNumbers(firstNum: 1.0, secondNum: 100.0))
yAxis.append(yVal)
}
setData(xAxisArray: xAxis, yAxisArray: yAxis, chart: liveChart)
let testView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testView.center = view.center
testView.backgroundColor = UIColor.red
view.addSubview(testView)
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
}
func configureChart(chart : LineChartView)
{
chart.chartDescription?.text = ""
chart.noDataText = "Loading Data"
chart.backgroundColor = UIColor.clear
chart.drawGridBackgroundEnabled = false
chart.dragEnabled = true
chart.rightAxis.enabled = false
chart.leftAxis.enabled = true
chart.doubleTapToZoomEnabled = false
chart.legend.enabled = false
chart.pinchZoomEnabled = true
chart.highlightPerTapEnabled = false
chart.highlightPerDragEnabled = false
chart.xAxis.enabled = false
chart.leftAxis.drawAxisLineEnabled = false
chart.leftAxis.drawGridLinesEnabled = false
chart.leftAxis.labelCount = 5
chart.leftAxis.forceLabelsEnabled = true
}
func setData(xAxisArray : [String], yAxisArray : [Double], chart : LineChartView)
{
var yVals1 : [ChartDataEntry] = [ChartDataEntry]()
if(xAxisArray.count > 0)
{
for i in 0 ..< xAxisArray.count
{
let chartEntry = ChartDataEntry(x: Double(i), y: yAxisArray[i], data: nil)
yVals1.append(chartEntry)
}
}
let set1: LineChartDataSet = LineChartDataSet(values: yVals1, label: "")
set1.fillColor = UIColor.red
set1.drawFilledEnabled = true
set1.drawCirclesEnabled = false
let data = LineChartData()
data.addDataSet(set1)
liveChart.data = data
}
Is there a way to fix this? Or is this just the way the fill colour of the chart works?
Edit:
I am using
https://github.com/danielgindi/Charts
I assume you use this library: https://github.com/kevinbrewster/SwiftCharts
So the LineChartView automatically set alpha for the fill color: https://github.com/kevinbrewster/SwiftCharts/blob/master/SwiftCharts/LineChart.swift#L758
try to set fillAlpha property of the data set