I want to make a round image on map. Image size is 200 *200px, but something is wrong with my cornerRadius after transform. Can someone help me?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annoView = MKAnnotationView(annotation: selectedAnnotation, reuseIdentifier: "")
if (annotation is MKUserLocation){
return nil
}
if(annotation.title! == "Hej"){
annoView.image = UIImage(named: "Hej")
let transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
annoView.transform = transform
annoView.layer.cornerRadius = 100
annoView.layer.masksToBounds = true
let label = UILabel(frame: CGRect(x: annoView.frame.size.width/2, y: annoView.frame.size.height/2, width: 200,height: 30))
label.textColor = #colorLiteral(red: 0.7215686275, green: 0.8862745098, blue: 0.5921568627, alpha: 1)
label.font = UIFont.boldSystemFont(ofSize: 30)
label.center = CGPoint(x: 100, y: 0)
label.textAlignment = .center
label.text = "Hej"
annoView.addSubview(label)
}
}
Related
I tried to create custom segmented control which should change its look if you are hovering it. But it does not work as I expected.
Here's the code (I am sorry that it is that long but I already made it 200 lines shorter then it was):
class MySegmentedControl: UIView {
var state = MySegmentedControl_State.unknown
var buttons = [MySegmentedControl_ButtonView]()
func setUpView(){
let view = UIView(frame: self.frame)
view.frame.origin.x = 0
view.frame.origin.y = 0
view.addSubview(holderView())
self.addSubview(view)
}
func holderView() -> UIView{
let view = UIView(frame: CGRect(origin: CGPoint(x: 100, y: 0), size: CGSize(width: 490, height: 70)))
view.layer.borderWidth = 5
view.layer.borderColor = UIColor.gray.cgColor
view.layer.cornerRadius = view.frame.height / 2
view.layer.masksToBounds = true
view.clipsToBounds = true
view.backgroundColor = #colorLiteral(red: 0.5741485357, green: 0.5741624236, blue: 0.574154973, alpha: 1)
//place the first button
let button_1 = MySegmentedControl_ButtonView(text: "One", frame: CGRect(x: 0, y: 0, width: 163.3333, height: 70), type: .one, delegate: self)
//place the second Button
let button_2 = MySegmentedControl_ButtonView(text: "Two", frame: CGRect(x: 163.3333, y: 0, width: 163.3333, height: 70), type: .two, delegate: self)
//place the third Button
let button_3 = MySegmentedControl_ButtonView(text: "Three", frame: CGRect(x: 163.3333*2, y: 0, width: 163.3333, height: 70), type: .three, delegate: self)
buttons.append(button_1); buttons.append(button_2); buttons.append(button_3)
view.addSubview(button_1)
view.addSubview(button_2)
view.addSubview(button_3)
return view
}
class MySegmentedControl_ButtonView: UIView{
private var selected = false
private var hovering = false
var text = ""
private var type: MySegmentedControl_State
private var delegate: MySegmentedControl_ButtonView_Delegate
private var label = UILabel()
func setUpView(){
layer.cornerRadius = frame.height/2
let label = UILabel(frame: frame)
self.label = label
label.tintColor = .white
label.text = text
label.sizeToFit()
label.center = self.center
label.font = label.font.withSize(15)
let regionizer = UIHoverGestureRecognizer(target: self, action: #selector(didHover(_:)))
addGestureRecognizer(regionizer)
addSubview(label)
//set up the button
let button = UIButton(frame: bounds, primaryAction: UIAction(handler: { [self] action in
selected = true
delegate.shouldChangeState(to: type)
delegate.shouldDeselectOthers(without: text)
if !hovering{
backgroundColor = #colorLiteral(red: 0.9961533629, green: 0.9931518435, blue: 1, alpha: 0.8)
}else{
self.backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.9435433103)
}
}))
addSubview(button)
}
func deselect(){
self.selected = false
if hovering{
backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}else{
backgroundColor = #colorLiteral(red: 0.2605174184, green: 0.2605243921, blue: 0.260515637, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}
}
#objc
func didHover(_ recognizer: UIHoverGestureRecognizer){
switch recognizer.state{
case .began, .changed:
hovering = true
if selected{
backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}else{
backgroundColor = #colorLiteral(red: 0.2605174184, green: 0.2605243921, blue: 0.260515637, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}
case .ended:
hovering = false
if selected{
self.backgroundColor = #colorLiteral(red: 0.9961533629, green: 0.9931518435, blue: 1, alpha: 0.2)
UIView.animate(withDuration: 0.2) {[self]in
label.center.y = center.y
label.font = label.font.withSize(15)
}
}else{
self.backgroundColor = .clear
UIView.animate(withDuration: 0.2) {[self]in
label.center.y = center.y
label.font = label.font.withSize(15)
}
}
default:break
}
}
init(text: String, frame: CGRect, type: MySegmentedControl_State, delegate: MySegmentedControl_ButtonView_Delegate){
self.type = type
self.delegate = delegate
super.init(frame: frame)
self.text = text
setUpView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}
extension MySegmentedControl: MySegmentedControl_ButtonView_Delegate{
func shouldDeselectOthers(without: String) {
for button in buttons{
if button.text != without{
button.deselect()
}
}
}
func shouldChangeState(to state: MySegmentedControl_State) {
self.state = state
}
}
protocol MySegmentedControl_ButtonView_Delegate{
func shouldDeselectOthers(without: String)
func shouldChangeState(to state: MySegmentedControl_State)
}
enum MySegmentedControl_State: String{
case unknown = "Non specific case avalaible"
case one = "One selected"
case two = "Two selected"
case three = "Three selected"
}
But the second of my buttons is displayed as the third and the the third does not get displayed, but if I hover the place where button two SHOULD be, it still gets hovered.
Here is a shout video:
Video showing problems with this code
My App is running on MacOS with MacCatalyst
In setUpView() inside class MySegmentedControl_ButtonView, you are trying to set the label's position with:
label.center = self.center
However, self.center is relative to self's superview. So your labels are being shifted.
If you change that line to:
label.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
the label centering will be correct.
As I asked in my comment though... why aren't you using auto-layout? It would make things much easier (and much more flexible).
can someone tell me please how to make the button rounded, shadow and gradient
here I set the gradient and shadow to the button, but without rounding:
#IBOutlet weak var info: UIButton!
info.setTitle("INFO", for: .normal)
info.setTwoGradients(colorOne: Colors.OrangeGrad, colorTwo: Colors.OrangeGradSec)
info.setTitleColor(UIColor.black, for: .normal)
info.layer.shadowColor = UIColor.darkGray.cgColor
info.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
info.layer.shadowOpacity = 0.8
info.layer.shadowRadius = 2
view.addSubview(info)
I know that the shadow disappears because of the rounding and I found a way to fix it, ex:
final class CustomButton: UIButton {
private var shadowLayer: CAShapeLayer!
override func layoutSubviews() {
super.layoutSubviews()
if shadowLayer == nil {
shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 5.0, height: 2.0)
shadowLayer.shadowOpacity = 0.7
shadowLayer.shadowRadius = 2
layer.insertSublayer(shadowLayer, at: 0)
}
}
but there is a line:
shadowLayer.fillColor = UIColor.white.cgColor
because of which I can't set the gradient
therefore, I cannot find a way by which all three conditions would be met
Output:
Usage
class ViewController: UIViewController {
#IBOutlet var btnGradient: CustomButton!
override func viewDidLoad() {
super.viewDidLoad()
btnGradient.gradientColors = [.red, .green]
btnGradient.setTitle("Gradient Button", for: .normal)
btnGradient.setTitleColor(.white, for: .normal)
}
}
Custom Class
Use this class as a reference to setup the attributes:
class CustomButton: UIButton {
var gradientColors : [UIColor] = [] {
didSet {
setupView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
let startPoint = CGPoint(x: 0, y: 0.5)
let endPoint = CGPoint(x: 1, y: 0.5)
var btnConfig = UIButton.Configuration.plain()
btnConfig.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: layer.frame.height / 2, bottom: 5, trailing: layer.frame.height / 2)
self.configuration = btnConfig
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
//Gradient
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = gradientColors.map { $0.cgColor }
gradientLayer.startPoint = startPoint
gradientLayer.endPoint = endPoint
gradientLayer.cornerRadius = layer.frame.height / 2
if let oldValue = layer.sublayers?[0] as? CAGradientLayer {
layer.replaceSublayer(oldValue, with: gradientLayer)
} else {
layer.insertSublayer(gradientLayer, below: nil)
}
//Shadow
layer.shadowColor = UIColor.darkGray.cgColor
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.frame.height / 2).cgPath
layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
layer.shadowOpacity = 0.7
layer.shadowRadius = 2.0
}
}
You can use UIButton Extension or refer code
Pass colors in array with start & end point of gradient effect, you want to start & end. i.e. x=0, y=0 means TopLeft & x=1, y=1 means BottomRight
extension UIButton {
func setGradientLayer(colorsInOrder colors: [CGColor], startPoint sPoint: CGPoint = CGPoint(x: 0, y: 0.5), endPoint ePoint: CGPoint = CGPoint(x: 1, y: 0.5)) {
let gLayer = CAGradientLayer()
gLayer.frame = self.bounds
gLayer.colors = colors
gLayer.startPoint = sPoint
gLayer.endPoint = ePoint
gLayer.cornerRadius = 5
gLayer.shadowOpacity = 0.8
gLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
layer.insertSublayer(gLayer, at: 0)
}
}
I have created a messaging app and I want to set the border colour as a gradient instead of just a solid colour.
So far when I run my code this is what is see:
The gradient colour is supposed to be a border colour for each message cell
I don't know what is making it look the way it does
this is how I coded it :
I've created an extension to deal with the gradient and it looks like this:
extension UIView {
func gradientButton( startColor:UIColor, endColor:UIColor) {
let view:UIView = UIView(frame: self.bounds)
let gradient = CAGradientLayer()
gradient.colors = [startColor.cgColor, endColor.cgColor]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = self.bounds
self.layer.insertSublayer(gradient, at: 0)
self.mask = view
view.layer.borderWidth = 2
}
}
and when I set the gradient I set it like this in my chatController class
class ChatController: UICollectionViewController, UICollectionViewDelegateFlowLayout, ChatCellSettingsDelegate {
func configureMessage(cell: ChatCell, message: Message) {
cell.bubbleView.gradientButton(startColor: blue, endColor: green)
}
The bubbleView holds the text which is a textView of the message which is created in my ChatCell class:
class ChatCell: UICollectionViewCell {
let bubbleView: UIView = {
let bubble = UIView()
bubble.backgroundColor = UIColor.rgb(red: 0, green: 171, blue: 154, alpha: 1)
bubble.translatesAutoresizingMaskIntoConstraints = false
bubble.layer.masksToBounds = true
bubble.layer.cornerRadius = 13
bubble.backgroundColor = .white
return bubble
}()
let textView: UITextView = {
let text = UITextView()
text.text = "test"
text.font = UIFont.systemFont(ofSize: 16)
text.backgroundColor = .clear
text.translatesAutoresizingMaskIntoConstraints = false
text.isEditable = false
return text
}()
}
how do I fix this?
any help would be helpful
thank you
I can set the border colour to a solid colour with the following line:
cell.bubbleView.layer.borderColor = UIColor.rgb(red: 91, green: 184, blue: 153, alpha: 0.8).cgColor
and then it would look like this:
You can create your own bordered view and use it in your custom cell:
import UIKit
import PlaygroundSupport
public class GradientBorder: UIView {
var startColor: UIColor = .black
var endColor: UIColor = .white
var startLocation: Double = 0.05
var endLocation: Double = 0.95
var path: UIBezierPath!
let shape = CAShapeLayer()
var lineWidth: CGFloat = 5
override public class var layerClass: AnyClass { CAGradientLayer.self }
var gradientLayer: CAGradientLayer { layer as! CAGradientLayer }
func update() {
gradientLayer.startPoint = .init(x: 0.0, y: 0.5)
gradientLayer.endPoint = .init(x: 1.0, y: 0.5)
gradientLayer.locations = [startLocation as NSNumber, endLocation as NSNumber]
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
path = .init(roundedRect: bounds.insetBy(dx: lineWidth/2, dy: lineWidth/2), byRoundingCorners: [.topLeft, .bottomLeft, .topRight, .bottomRight], cornerRadii: CGSize(width: frame.size.height / 2, height: frame.size.height / 2))
shape.lineWidth = lineWidth
shape.path = path.cgPath
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
gradientLayer.mask = shape
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
update()
}
}
Playground testing:
let gradientBorderedView = GradientBorder()
gradientBorderedView.frame = .init(origin: .zero, size: .init(width: 200, height: 40))
let view = UIView()
view.frame = .init(origin: .zero, size: .init(width: 375, height: 812))
view.backgroundColor = .blue
view.addSubview(gradientBorderedView)
PlaygroundPage.current.liveView = view
I Create a custom MKAnnotationView, that works fine but it pops as a rectangle, without the triangle (the same one you have in comics when someone is talking), that comes out of the pin.
Is there a way that the triangle will be added automatically?
here is my MKAnnotationView subclass:
class ShikmimAnnotationView: MKAnnotationView{
let selectedLabel:UILabel = UILabel.init(frame:CGRect(x: 0, y: 0, width: 140, height: 40))
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(false, animated: animated)
if(selected)
{
// Do customization, for example:
//let defaults = UserDefaults.standard
selectedLabel.font = UIFont(name: "Heebo-Regular.ttf", size: 18)
selectedLabel.text = "(גדליהו אלון 13, ירושלים\n (3 דק' הגעה"
selectedLabel.numberOfLines = 3
selectedLabel.sizeToFit()
selectedLabel.textColor = UIColor.white
selectedLabel.textAlignment = .center
selectedLabel.backgroundColor = UIColor.darkGray
selectedLabel.layer.borderColor = UIColor.white.cgColor
selectedLabel.layer.borderWidth = 2
selectedLabel.layer.cornerRadius = 5
selectedLabel.layer.masksToBounds = true
selectedLabel.center.x = 0.5 * self.frame.size.width;
selectedLabel.center.y = -0.5 * selectedLabel.frame.height;
self.addSubview(selectedLabel)
self.image = UIImage(named: "map_pin_next_stop-2.png")
}
else
{
selectedLabel.removeFromSuperview()
}
}
}
Here is how I initialize it in my VC:
internal func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? MyLocation {
let identifier = "reuse"
var view: ShikmimAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier){
dequeuedView.annotation = annotation
view = dequeuedView as! ShikmimAnnotationView
} else {
view = ShikmimAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.image = UIImage(named: "map_pin_next_stop-2.png")
view.canShowCallout = false
//view.calloutOffset = CGPoint(x: -5, y: 5)
//view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
}
return view
}
return nil
}
You can draw this triangle with BezierPath. Here's class for this view. You can setup size of view in storyboard or make it programmatically in your ShikmimAnnotationView class.
class TriangleView: UIView {
let shapeLayer = CAShapeLayer()
override func drawRect(rect: CGRect) {
// Get Height and Width
let layerHeight = self.layer.frame.height
let layerWidth = self.layer.frame.width
// Create Path
let bezierPath = UIBezierPath()
// Draw Points
bezierPath.moveToPoint(CGPoint(x: 0, y: 0))
bezierPath.addLineToPoint(CGPoint(x: layerWidth, y: 0))
bezierPath.addLineToPoint(CGPoint(x: layerWidth / 2, y: layerHeight))
bezierPath.addLineToPoint(CGPoint(x: 0, y: 0))
bezierPath.closePath()
// Apply Color
UIColor(red: (2/255), green: (35/255), blue: (73/255), alpha: 1).setFill()
bezierPath.fill()
// Mask to Path
shapeLayer.path = bezierPath.CGPath
self.layer.mask = shapeLayer
}
}
I am using expandable tableView, which is show/hide on click sectionHeader.
As I want to add Label/Button on HeaderView. but at the time of scrolling the label show/hide automatically. but I doesn't want label is disappear on scroll of tableView.
My Code is -
func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
// Background view is at index 0, content view at index 1
if let bgView = view.subviews[0] as? UIView
{
if section == 0 {
let labelValue = UILabel(frame: CGRect(x: 5, y: 80, width: 40, height: 20))
labelValue.backgroundColor = UIColor.greenColor()
bgView.addSubview(labelValue)
}
if section == 1{
let labelValue = UILabel(frame: CGRect(x: 5, y: 80, width: 40, height: 20))
labelValue.backgroundColor = UIColor.redColor()
bgView.addSubview(labelValue)
}
if section == 2{
let labelValue = UILabel(frame: CGRect(x: 5, y: 80, width: 40, height: 20))
labelValue.backgroundColor = UIColor.yellowColor()
bgView.addSubview(labelValue)
}
if section == 3{
let labelValue = UILabel(frame: CGRect(x: 5, y: 80, width: 40, height: 20))
labelValue.backgroundColor = UIColor.blueColor()
bgView.addSubview(labelValue)
}
}
view.layer.borderColor = UIColor.blackColor().CGColor
view.layer.borderWidth = 0.3
}
ViewForHeaderSection
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = HeaderView(tableView: self.customTableView, section: section)
headerView.backgroundColor = UIColor.whiteColor()
let label = UILabel(frame: headerView.frame)
label.textAlignment = NSTextAlignment.Center
label.font = UIFont (name: "HelveticaNeue-Light", size: 16)
label.textColor = UIColor.blackColor()
let labelValue = UILabel(frame: CGRect(x: 14, y: 15, width: 43, height: 32))
labelValue.textAlignment = NSTextAlignment.Center
labelValue.textColor = UIColor.whiteColor()
labelValue.font = UIFont(name: "HelveticaNeue-Bold", size: 13)
labelValue.text = "14"
//let btnExpand = UIButton(frame: headerView.frame)
let btnExpand = UIButton(frame:CGRect(x: 320, y: 17, width: 24, height: 24))
btnExpand.setBackgroundImage(UIImage(named: "expand"), forState: .Normal)
btnExpand.addTarget(self, action: #selector(HomeViewController.expandTableView), forControlEvents: .TouchUpInside)
if section == 0 {
label.text = "Runing Vehicle"
labelValue.backgroundColor = UIColor(red: 14/255.0, green: 76/255.0, blue: 12/255.0, alpha: 1.0)
btnExpand.addTarget(self, action: #selector(HomeViewController.expandTableView), forControlEvents: .TouchUpInside)
}
if section == 1 {
label.text = "Idle Vehicle"
labelValue.backgroundColor = UIColor(red: 148/255.0, green: 0/255.0, blue: 11/255.0, alpha: 1.0)
}
if section == 2 {
label.text = "Vehicle AT POI"
labelValue.backgroundColor = UIColor(red: 162/255.0, green: 136/255.0, blue: 5/255.0, alpha: 1.0)
}
if section == 3 {
label.text = "All Vehicle"
labelValue.backgroundColor = UIColor(red: 31/255.0, green: 61/255.0, blue: 127/255.0, alpha: 1.0)
}
headerView.addSubview(label)
headerView.addSubview(labelValue)
headerView.addSubview(btnExpand)
return headerView
}
func expandTableView(){
print("Hello")
customTableView.contentSize = CGSizeMake(375, 667)
}
You need to add that small label with that background color inside viewForHeaderInSection method because with willDisplayHeaderView it will add that label multiple time, so change your viewForHeaderInSection and remove the willDisplayHeaderView method.
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = HeaderView(tableView: self.customTableView, section: section)
headerView.backgroundColor = UIColor.whiteColor()
let label = UILabel(frame: headerView.frame)
label.textAlignment = NSTextAlignment.Center
label.font = UIFont (name: "HelveticaNeue-Light", size: 16)
label.textColor = UIColor.blackColor()
let labelValue = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: headerView.frame.size.height))
if section == 0 {
label.text = "Runing Vehicle"
labelValue.backgroundColor = UIColor.greenColor()
}
if section == 1 {
label.text = "Idle Vehicle"
labelValue.backgroundColor = UIColor.redColor()
}
if section == 2 {
label.text = "Vehicle AT POI"
labelValue.backgroundColor = UIColor.yellowColor()
}
if section == 3 {
label.text = "All Vehicle"
labelValue.backgroundColor = UIColor.blueColor()
}
headerView.addSubview(label)
headerView.addSubview(labelValue)
return headerView
}
Edit: Change your last line of code that return the headerView like this
headerView.addSubview(label)
headerView.addSubview(labelValue)
headerView.addSubview(btnExpand)
headerView.sendSubviewToBack(label)
headerView.bringSubviewToFront(btnExpand)
return headerView