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.
Related
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!
let centerPointX = colorSizeGuide.bounds.midX / 2
let centerPointY = colorSizeGuide.bounds.midY / 2
let circleWidth: CGFloat = 10
let circleHeight: CGFloat = 10
shape.path = UIBezierPath(ovalIn: CGRect(x: centerPointX + circleWidth / 4, y: centerPointY + circleHeight / 4, width: circleWidth, height: circleHeight)).cgPath
shape.strokeColor = UIColor(r: 160, g: 150, b: 180).cgColor
shape.fillColor = UIColor(r: 160, g: 150, b: 180).cgColor
shape.anchorPoint = CGPoint(x: 0.5, y: 0.5)
shape.lineWidth = 0.1
shape.transform = CATransform3DMakeScale(4.0, 4.0, 1.0)
colorSizeGuide.layer.addSublayer(shape)
Here's what's happening. I need the CAShapeLayer to stay in the middle of the small gray area:
I struggle with affine transforms a little myself, but here's what I think is going on:
The scale takes place centered around 0,0, so it will grow out from that point. That means it will "push away" from the origin.
In order to grow from the center, you should shift the origin to the center point of your shape, scale, and then shift the origin back, by the now-scaled amount:
var transform = CATransform3DMakeTranslation(centerPointX, centerPointY, 0)
transform = CATransformScale(transform, 4.0, 4.0, 1.0)
var transform = CATransform3DTranslate(
transform,
-4.0 * centerPointX,
-4.0 * centerPointY,
0)
shape.transform = transform
BTW, I can't make any sense of the image you posted with your question. You say "I need the CAShapeLayer to stay in the middle of the small gray area" I gather your shape layer is one of the circles, but it isn't clear what you mean by "the small gray area." It looks like there might be an outline that got cropped somehow.
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.
I'm drawing bar graphs, and I have several stacked CGRects that are directly on top of each other (i.e. one rect's minY is the previous rect's maxY). However, there are still semi-transparent gaps between the rects. Is there any way to fix this? I've found that this also happens when drawing touching adjacent arcs.
Here's a screenshot of what I mean:
By zooming in, I've confirmed that this isn't just an optical illusion like one would find between adjacent red and blue rects. I would appreciate any input.
var upToNowSegmentTotal: CGFloat = 0
for k in 0..<data[i].bars[j].segments.count {
var segmentRect: CGRect = CGRect()
if barDirection == "vertical" {
let a: CGFloat = translateY(upToNowSegmentTotal)
let b: CGFloat = translateY(upToNowSegmentTotal + data[i].bars[j].segments[k].length)
upToNowSegmentTotal += data[i].bars[j].segments[k].length
var rectY: CGFloat
if a > b {
rectY = b
} else {
rectY = a
}
segmentRect = CGRect(
x: barWidthPosition,
y: rectY,
width: barWidthAbsolute,
height: abs(a - b)
)
}
}
Ignore the stuff about the width of the bars. Here's the translateY function. Basically, it translates coordinates from the graphing window into x/y stuff that's drawn. Remember that because the window/ graphing area does not change between drawn rects, the same y input will always produce the same result.
private func translateY(y: CGFloat) -> CGFloat {
if barsAreReversed {
return graphingArea.minY + graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length)
} else {
return graphingArea.maxY - graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length)
}
}
EDIT 2:
Here's a simplified version of my code that shows the problem:
override func drawRect(rect: CGRect) {
let rect1: CGRect = CGRect(
x: 0,
y: 0,
width: 40,
height: 33.7
)
let rect2: CGRect = CGRect(
x: 0,
y: rect1.height,
width: 40,
height: 33.5
)
let context: CGContextRef = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor(red: 1 / 255, green: 29 / 255, blue: 29 / 255, alpha: 1).CGColor)
CGContextAddRect(context, rect1)
CGContextFillRect(context, rect1)
CGContextSetFillColorWithColor(context, UIColor(red: 9 / 255, green: 47 / 255, blue: 46 / 255, alpha: 1).CGColor)
CGContextAddRect(context, rect2)
CGContextFillRect(context, rect2)
}
It produces this:
I suspect that in this particular case, the rects you are filling are not integral, i.e they might have origins/heights that are by default rendered with slightly transparent pixels (anti-aliasing). You could avoid this by properly rounding your Y-axis translation
private func translateY(y: CGFloat) -> CGFloat {
if barsAreReversed {
return round(graphingArea.minY + graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length))
} else {
return round(graphingArea.maxY - graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length))
}
}
With arcs and other shapes it is not as easy, however, you could try and get rid of it, by leaving a bit of overlap between shapes. Of course, as pointed out by matt, you could simply turn anti-aliasing off, in which case these transparent "half-pixels" will all be rendered as if they are actually fully-within the rect.
This is likely happening because the rectangle coordinates you are using to draw shapes are fractional values. As a result Core Graphics performs antialiasing at the edges of those rectangles when your coordinates land between pixel boundaries.
You could solve this by simply rounding the coordinates of the rectangles before drawing. You can use the CGRectIntegral function which performs this kind of rounding, for example:
CGContextFillRect(context, CGRectIntegral(rect1))
It's antialiasing. I can prevent this phenomenon by using your exact same code but drawing in a CGContext in which we have first called CGContextSetAllowsAntialiasing(context, false). Here it is without that call:
And here it is with that call:
But, as others have said, we can get the same result by changing your 33.7 and 33.5 to 40, so that we come down on pixel boundaries.
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.