Resize parent UIView based on its children size - ios

I'm new in iOS programming and still don't know how to handle correctly basic stuff like this so I hope someone has good practice to share.
I created a custom keyboard for the user to answer to a question.
It's an UIView containing some UIButton.
How does one center the whole keyboard?
I know I could calculate the width of the keyboard by adding up the width of each key but this seems quite complicated for such a simple action.
Here's my code (in Swift) for now, thanks.
keyboardContainer = UIView(frame: CGRectMake(0, 0, self.frame.width, 200))
keyboardContainer!.center = CGPoint(x: keyboardContainer!.frame.width / 2, y: self.frame.height - keyboardContainer!.frame.height / 2)
self.addSubview(keyboardContainer!)
let gap = Int(self.frame.width) / 8
for i in 0..<keyboardLetters!.count {
let posX = Int(i % 7) * gap + 22
let posY = Int(i / 7) * gap + 25
let key = Key(frame: CGRectMake(0, 0, 44, 44))
key.letter = keyboardLetters![i]
key.tag = i
key.center = CGPoint(x: posX, y: posY)
keyboardContainer!.addSubview(key)
key.addTarget(self, action: "onKeyboardTap:", forControlEvents: .TouchUpInside)
}

Move the center of keyboardContainer and all the buttons will move with it.

Related

iOS Swift Why can't height of UILabel text be made bigger?

I want the UILabel text to be larger, but when I change value_row_height, there is no change in size.
var value_row_x = 30
var value_row_y = 100
var value_row_height = 320
var value_row_width = 30
let heart_rate_title_UILabel = UILabel(frame: CGRect(x: value_row_x, y: value_row_y, width: value_row_width, height: value_row_height ))
heart_rate_title_UILabel.center = CGPoint( x: value_row_x + value_row_width / 2, y: value_row_y + value_row_height/2 )
heart_rate_title_UILabel.textAlignment = .left
heart_rate_title_UILabel.text = "heart: "
heart_rate_title_UILabel.sizeToFit()
self.view.addSubview( heart_rate_title_UILabel)
UPDATE:
I added following code, but size failed to change...
heart_rate_title_UILabel.font = label.font.withSize(20)
heart_rate_title_UILabel.minimumScaleFactor = 0.1 //or whatever suits your need
heart_rate_title_UILabel.adjustsFontSizeToFitWidth = true
heart_rate_title_UILabel.lineBreakMode = .byClipping
heart_rate_title_UILabel.numberOfLines = 0
You are changing the physical height of the label. You are not saying anything about what the text size should be. Text size is a matter of setting the label's font (which includes size).
This worked jim-dandy....
value_row_y -= 10
value_row_x += 45 + inter_value_gap_pix // + width of heart rate value
let title_UILabel = UILabel(frame: CGRect(x: value_row_x, y: value_row_y, width: value_row_width, height: value_row_height ))
title_UILabel.center = CGPoint( x: value_row_x + value_row_width / 2, y: value_row_y + value_row_height/2 )
title_UILabel.textAlignment = .left
title_UILabel.text = " RSP: "
self.view.addSubview( title_UILabel)
// set font height to biggest (ie. label height)...
title_UILabel.numberOfLines = 1;
title_UILabel.font = title_UILabel.font.withSize(30)

Making a very simple graph with uibezierpath

my question was
I want to create a simple line graph with certain values. This is done in a view within the mainviewcontroller. I created a UIview named chart. I pass the data to the chart when its retrieved from the API. I figured out how to draw the axis but I am stuck now. I cant find anything on google on how to set labels on intervals and to make the points appear dynamically.
draw the xasis and its labels.
draw the dots in the graph.
My salution
i figured out how to do all the things i asked for.
The code I have now:
class ChartView: UIView {
//some variables
var times: [String] = []
var AmountOfRain: [Double] = []
let pathy = UIBezierPath()
let pathx = UIBezierPath()
var beginwitharray = Array<CGFloat>()
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
//draw the y line
pathy.move(to: CGPoint(x: 30, y: 10))
pathy.addLine(to: CGPoint(x: 30, y: 10))
pathy.addLine(to: CGPoint(x: 30, y: frame.size.height - 30))
UIColor.black.setStroke()
pathy.lineWidth = 1.0
pathy.stroke()
//draw the x line
pathx.move(to: CGPoint(x: 30, y: frame.size.height - 30))
pathx.addLine(to: CGPoint(x: 30, y: frame.size.height - 30))
pathx.addLine(to: CGPoint(x: frame.size.width - 30, y: frame.size.height - 30))
UIColor.black.setStroke()
pathx.lineWidth = 1.0
pathx.stroke()
//when the data arrives form the SUPER slow duienradar API refresh it with the data
if beginwitharray != []{
//remove the label retriving data
let label = viewWithTag(1)
DispatchQueue.main.sync {
label?.removeFromSuperview()
}
//create the dots in the graph
var point = CGPoint()
//simple way to do 2 loop in 1 loop.
var intforbeginarray = 0
let stoke = UIBezierPath()
//get the first 6 itmes out of the rain array cuz of space issues
let first6aumountarray = AmountOfRain[0...5]
stoke.move(to: CGPoint(x: 30, y: self.frame.size.height - 30))
//loop trough the data in the amounts array
for amount in first6aumountarray{
//determen the hight of the dot
let InitialHeight = (CGFloat(amount) * (self.frame.size.height - 30))/6
let pointHeight = (frame.size.height - 30) - InitialHeight
//make the point so we can draw it using UIbezierpath()
point = CGPoint(x: beginwitharray[intforbeginarray] + 20, y: pointHeight)
intforbeginarray += 1
//create the dot
let dot = UIBezierPath()
dot.addArc(withCenter: point, radius: CGFloat(5), startAngle: CGFloat(0), endAngle: CGFloat(360), clockwise: true)
UIColor.black.setFill()
dot.lineWidth = 30
dot.fill()
//create the line between dots will give a warning on the last one cuz the last one doenst go anyway
stoke.addLine(to: point)
stoke.move(to: point)
stoke.lineWidth = 1
UIColor.black.setStroke()
}
//make the strokes
stoke.stroke()
}
}
func getvalues(RainData: [Double], TimesData:[String]){
//assing the data to the subview
self.AmountOfRain = RainData
self.times = TimesData
//xaxis values
let maxint = [0, 1, 2, 3, 4, 5, 6]
//calculate the hight spacing to fit the graph
let heightperstep = ((self.frame.size.height - 5)/6)-5
var beginheight = self.frame.size.height - 35
//calculate the width spacing to fit the graph
let widthperstep = ((self.frame.size.width - 5)/6)-5
var beginwith = CGFloat(30)
//extra check to see if we have data at all.
if times != []{
//get the first 6 items out of the times array for use in our graph
let first6 = times[0...5]
//draw the label on the main queue
DispatchQueue.main.sync {
//draw the xaxis labels accroding to the spacing
for number in maxint{
let label = UILabel(frame: CGRect(x: 5, y: beginheight, width: 25, height: 15))
label.text = "\(number)"
self.addSubview(label)
beginheight = beginheight - heightperstep
}
//draw the yaxis labels according to the spacing
for time in first6{
let label = UILabel(frame: CGRect(x: beginwith, y: self.frame.size.height - 20, width: 55, height: 15))
label.text = time
self.addSubview(label)
beginwitharray.append(beginwith)
beginwith = beginwith + widthperstep
}
}
}
//redrawthe graph with new data.
setNeedsDisplay()
}}
Any help would be appreciated. I also can't use a lib or a pod since this is a school project and I need to create a simple graph.
EDIT:
Completed my code, cleared up an error when running this code
What I did first was to draw the x-asis and the y-axis. After this I considered reasonable values for the aumountofrain data. this turns out cannot really be higher then 6. Since I could fit around 6 labels in the space I have the steps where easy go down by 1 till I hit 0. The calculations I did are for my specific frame height. After I figured it all out and the padding for the y-asxis. It was a matter of figuring out how to get the dots in the right place. Since I already have the data in the beginwitharray I just needed to calculate the height. Then it was simply loop trough the data and draw each dot. Then I just had to connect the dots using the uibezierpath.
i hope my troubles will save someone a lot of time when they read how i done it.
This might be helpful: Draw Graph curves with UIBezierPath
Essentially what you need to do is for every data set you have you need to know the y-axis range of values and based on those ranges assign each value a CGFloat value (in your case inches of rain needs to correlate to a certain CGFloat value). Let's say you have your set amountOfRain = [0.1, 1.3, 1.5, 0.9, 0.1, 0] so your range is var rangeY = amountOfRain.max() - amountOfRain.min(). now lets find out where your first data point 0.1 should go on your graph by converting inches of rain to a CGFloat value that corresponds to the axis you've drawn already, this equation is just basic algebra: let y1 = (amountOfRain[0]/rangeY)*((frame.size.height-30) - 10) + 10 now it looks like your rain samples are at regular intervals so maybe let x1:CGFloat = 10 now you can add a dot or something at the CGPoint corresponding with (x1,y1). If you did this with all the data points it would create a graph that has your maximum value at the top of the graph and minimum value at the bottom. Good Luck!

How to make text the same size on all devices in SpriteKit?

In SpriteKit, is there a way to make an SKLabelNode look the same size, regardless of the device, eg: Looks the same size on a iPhone 5 as a 6Plus?
I've tried using this method someone else recommended:
let textRect = CGRect(x: 0, y: 0, width: frame.width * 0.4, height: frame.height * 0.045)
let scalingFactor = min(textRect.width / text.frame.width, textRect.height / text.frame.height)
text.fontSize *= scalingFactor
But it doesn't make all text the same size, as words like "man" aren't as physically tall as words like "High" (due to it's "y" and "h" sticking out).
So is there a method to make text look the same size on all devices? At the moment I create the SKLabelNode like so:
let text = SKLabelNode(text: "Start")
text.fontSize = 30
text.position = CGPoint(x: 0, y: 0)
addChild(text)
The issue here is that you are trying to scale the fontSize, and this does not really play well with complex decimal numbers. Instead, after you create your label, just scale that to the scale factor that you are using to scale everything else
let text = SKLabelNode(text: "Start")
text.fontSize = 30
text.position = CGPoint(x: 0, y: 0)
text.xScale = xScaleFactor
text.yScale = yScaleFactor
where xScaleFactor and yScaleFactor are the factors you are using to determine your scale. (This number should only have to be calculated once, and then stored, if you are not doing that, I would recommend making that change)
Basically in the code you provided it is done like this:
let textRect = CGRect(x: 0, y: 0, width: frame.width * 0.4, height: frame.height * 0.045)
let scaleFactorX = textRect.width / text.frame.width
let scaleFactorY = textRect.height / text.frame.height
I think it's more like an algorithm question. Think about you need to implement the same thing in TV, iPad or in the iPhone device. You should think about storing its absolute value rather than its actual value.
The formula should be width for store value = actual width for this device / device width. The same with the height. Then, if you use the same image data in other devices. You will just need to multiply the new device width/height.

SKShapeNode position line up with ground

I am simply trying to draw a rectangle and placing it in the bottom left hand side of the screen
var rectW:CGFloat = CGFloat(200.0)
var rectH:CGFloat = ceil(self.frame.height * 0.15)
var rect = SKShapeNode(rectOfSize: CGSize(width: rectW, height: rectH));
let posX:CGFloat = 0.0 + (rect.frame.width / 2)
let posY:CGFloat = self.frame.height - (self.frame.height - rect.frame.height)
rect.position = CGPointMake(posX,posY)
rect.fillColor = SKColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
rect.lineWidth = 1
self.addChild(rect)
The issue is that even though i believe all the maths is correct the rectangle is about 38 points/pixels that are under the screen, as you can see from the image below (the white border shows the limit).
For your information here is an output of the positions, heights and widths.
PosX: 100.5
PosY: 117.0
Rect Width: 201.0
Rect Height: 117.0
Frame Height: 768.0
Frame Weight: 1024.0
If i simple take this line but add 38 points/pixels it works, but why? Is there something I am missing?
let posY:CGFloat = self.frame.height - (self.frame.height - rect.frame.height) + 38
I found the answer via the following two answers:
SpriteKit coordinate system messed up
Problems understanding coordinate system SpriteKit using Swift
The problem turns out to be with GameScene.sks having it's size set differently.
Open up GameScene.sks and go to 'Show SKNodeInspector' should be the third box in the top right hand side of the screen.
Then set the dimensions to 320 x 568
Save then run. Should be working.

Is the Bluetooth logo available as a character on iPhone?

Is there a font on iOS where there's a glyph for the Bluetooth logo? Some Dingbats, maybe, or Emoji? How about the WiFi logo?
EDIT: how about a third party font where there's such a character, the one that I could license and ship?
No, the Bluetooth logo is not a glyph or a font-face character.
Like Seva said, It's a combination of runic Hagall (ᚼ) and Bjarkan (ᛒ). I was trying to do the same thing by combining the two symbols.
I accomplished this by first finding a font that had these two characters. I ended up using LeedsUni. You can download it here: http://www.personal.leeds.ac.uk/~ecl6tam/.
You'll need to reference the path where the font is located in info.plist.
<key>UIAppFonts</key>
<array>
<string>Fonts/LeedsUni10-12-13.ttf</string>
</array>
I then created two UIView objects(UIButton objects in my case) which overlapped each other so that the two characters lined up properly. Depending on the font you use, you may need to adjust x and y values for the UIView frames.
My code is in C# because I'm using Xamarin, but you should be able to do the same thing in Objective C or Swift.
UIView bleView = new UIView(new CGRect(0, 0, 34.0f, 34.0f));
string fontName = "LeedsUni";
UIButton bluetoothToSerialButton = new UIButton(UIButtonType.RoundedRect);
bluetoothToSerialButton.Frame = new CGRect(0, 0, 34.0f, 34.0f);
bluetoothToSerialButton.SetTitleColor(UIApplication.SharedApplication.Delegate.GetWindow().TintColor, UIControlState.Normal);
bluetoothToSerialButton.SetTitle("ᛒ", UIControlState.Normal);
bluetoothToSerialButton.Font = UIFont.FromName(fontName, 34.0f);
UIButton bluetoothToSerialButton2 = new UIButton(UIButtonType.RoundedRect);
bluetoothToSerialButton2.Frame = new CGRect(-3.5f, 0, 34.0f, 34.0f);
bluetoothToSerialButton2.SetTitleColor(UIApplication.SharedApplication.Delegate.GetWindow().TintColor, UIControlState.Normal);
bluetoothToSerialButton2.SetTitle("ᚼ", UIControlState.Normal);
bluetoothToSerialButton2.Font = UIFont.FromName(fontName, 34.0f);
bleView.AddSubviews(new UIView[] { bluetoothToSerialButton, bluetoothToSerialButton2 });
Using Quartz2D in Swift 4.1
If you hate using external fonts or adding a bunch of .png files, you may prefer a simple class to get the same effect using Quartz2D.
This works for me with a logo of 20x20 points. You may want to optimize the geometry or line width. Also note that the frame's width should be equal the height.
Note that you will need to use setNeedsDisplay if the size changes.
import UIKit
class BluetoothLogo: UIView {
var color: UIColor!
convenience init(withColor color: UIColor, andFrame frame: CGRect) {
self.init(frame: frame)
self.backgroundColor = .clear
self.color = color
}
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let h = self.frame.height
let y1 = h * 0.05
let y2 = h * 0.25
context?.move(to: CGPoint(x: y2, y: y2))
context?.addLine(to: CGPoint(x: h - y2, y: h - y2))
context?.addLine(to: CGPoint(x: h/2, y: h - y1))
context?.addLine(to: CGPoint(x: h/2, y: y1))
context?.addLine(to: CGPoint(x: h - y2, y: y2))
context?.addLine(to: CGPoint(x: y2, y: h - y2))
context?.setStrokeColor(color.cgColor)
context?.setLineCap(.round)
context?.setLineWidth(2)
context?.strokePath()
}
}
These are the icons from google material design you can download them from here https://material.io/tools/icons/?icon=bluetooth&style=baseline
No emoji, just checked on my iPad.
Just use a PDF or EPS of the bluetooth logo if you wan't it scalable, or just use a png otherwise.

Resources