I am trying to add a UITextView to my view. I have this class:
import UIKit
class TextAnnotation: UITextView {
var lastLocation:CGPoint?
var panRecognizer:UIPanGestureRecognizer?
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
self.panRecognizer = UIPanGestureRecognizer(target:self, action: #selector(StampAnnotation.detectPan))
self.gestureRecognizers = [panRecognizer!]
self.text = "Text Field"
self.backgroundColor = UIColor.brownColor()
self.userInteractionEnabled = true
self.editable = true
self.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: 200, height: 50)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func detectPan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.superview!)
self.center = CGPointMake(lastLocation!.x + translation.x, lastLocation!.y + translation.y)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Promote the touched view
self.superview?.bringSubviewToFront(self)
// Remember original location
lastLocation = self.center
}
}
The background color will change to brown, but the text does not get added and when I tap on the UITextView nothing happens when I set to editable, what am I doing wrong?
Here is how I am calling this class:
let textCenter = touches.first!.locationInView(view)
let textView = TextAnnotation(frame: CGRect(x: textCenter.x, y: textCenter.y, width: 200, height: 50), textContainer: NSTextContainer())
view.addSubview(textView)
You are the one who has wantonly stripped off the real gesture recognizers of this text view and replaced them with something else, effectively crippling the text view. Don't do that. Don't mess with the gesture recognizers of a text view! If interaction is enabled and editable is true, this text view needs to be able to do its own work.
Related
I'm pretty new to Swift and I'm having some trouble displaying UITextField in SpriteKit. Init() in SceneMenu will not display the UITextField, while using function showTextField() with touchesBegan() works. How can I display UITextField without the user clicking the button Show TextField?
GameViewController.swift:
class GameViewController: UIViewController {
override func viewDidLoad() {
let scene = SceneMenu(size: view.frame.size)
scene.scaleMode = .aspectFill
scene.backgroundColor = .white
let view = view as! SKView
view.presentScene(scene)
}
}
SceneMenu.swift:
class SceneMenu: SKScene {
override init(size: CGSize) {
super.init(size: size)
let btnAlert = SKLabelNode(text: "Show TextField")
btnAlert.name = "btn_text"
btnAlert.fontSize = 20
btnAlert.fontColor = SKColor.blue
btnAlert.fontName = "Avenir"
btnAlert.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(btnAlert)
let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 300), size: CGSize(width: 200, height: 30))
let textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = SKColor.blue
textField.placeholder = "Type name"
view?.addSubview(textField)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showTextField() {
let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 30))
let textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = SKColor.blue
textField.placeholder = "Type name"
view?.addSubview(textField)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let action = atPoint(location)
if action.name == "btn_text" {
showTextField()
}
}
}
}
SKScene has a method that lets you setup the scene when you move to it. So, instead of using the custom class's initializer, override and use SKScene's didMove(to view: SKView) method. An example:
override func didMove(to view: SKView) {
showTextField()
}
I am trying to get random CGPoints along the 'outer contour' of UIView so that I will be able to draw UIBezierPath line over obtained CGPoints. For example, get CGPoints from below red dots which I marked myself by hand. Any idea?
###Edit###
As per Sweeper's advice I am trying to add CAShapeLayer into my custom view but it returns nil when I am printing its path. Please could you point out what I am missing?
class ViewController: UIViewController {
let shapeLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
let view = BubbleView(frame: CGRect(x: self.view.frame.midX / 2, y: self.view.frame.midY/2, width: 150, height: 100))
view.backgroundColor = .gray
view.layer.cornerRadius = 30
self.view.addSubview(view)
}
}
class BubbleView: UIView {
var pathLayer: CAShapeLayer!
override func layoutSubviews() {
pathLayer = CAShapeLayer()
pathLayer.bounds = frame
self.layer.addSublayer(pathLayer)
print(pathLayer.path) // returns nil
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I'm creating an darken overlay on top of my UITableView, I would like to highlight one of the row. I'm able to highlight it, but how can I enable user interaction (e.g. tap on the cell)?
func addFullScreenDarkOverlay(){
let viewDarkOverlay = UIView()
viewDarkOverlay.alpha = 0.5
viewDarkOverlay.backgroundColor = .black
viewDarkOverlay.frame = UIApplication.shared.keyWindow!.frame
// add dummy hole (will change it to the frame of the particular cell)
let holeFrame = CGRect(x: 0, y: 0, width: viewDarkOverlay.frame.width/2, height: viewDarkOverlay.frame.height/2)
// Do any additional setup after loading the view.
let path = CGMutablePath()
// Dark background
path.addRect(CGRect(x: 0, y: 0, width: viewDarkOverlay.frame.width, height: viewDarkOverlay.frame.height))
// White/spotlight path
path.addRect(holeFrame)
let maskLayer = CAShapeLayer()
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = kCAFillRuleEvenOdd
viewDarkOverlay.layer.mask = maskLayer
viewDarkOverlay.clipsToBounds = true
UIApplication.shared.keyWindow!.addSubview(viewDarkOverlay)
}
Basically, you can disable the user interaction of the mask view, that will pass the all the touch events to the view below... however, if you just want the highlighted area to be intractable (i.e. the cell), you can create a custom UIView subclass, override the hitTest method, like so:
import UIKit
class MaskView: UIView {
var hitableArea: CGRect {
return CGRect(x: 0, y: 0, width: bounds.width * 0.5, height: bounds.height * 0.5)
}
override init(frame: CGRect) {
super.init(frame: frame)
installMaskLayer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
installMaskLayer()
}
private func installMaskLayer() {
backgroundColor = UIColor.black.withAlphaComponent(0.5)
let path = CGMutablePath()
// Dark background
path.addRect(bounds)
// White/spotlight path
path.addRect(hitableArea)
let maskLayer = CAShapeLayer()
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
layer.mask = maskLayer
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if (hitableArea.contains(point)) {
return nil
}
return self
}
}
Just create this custom view and add it to your window, and now you can only touch the cell now.
Remove the user interaction in your overlay view and you will be able to select the rows in the view below it:
viewDarkOverlay.isUserInteractionEnabled = false
If you want the selection only inside the whole you can transfer the touch from the overlay view to your parent view using a delegate like so:
protocol OverlaySelection: class{
func selected(with touch: UITouch?)
}
class OverlayView: UIView{
weak var delegate : OverlaySelection?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
delegate?.selected(with: touches.first)
}
}
then in your parent view:
func addFullScreenDarkOverlay(){
let viewDarkOverlay = OverlayView()
viewDarkOverlay.delegate = self
//other config..
}
//Delegate
func selected(with touch: UITouch?) {
if let location = touch?.location(in: self.view){
//Here you check if this point is inside the whole and if it is you select
//the row, if not just return
let indexpath = tableview.indexPathForRow(at: location)
tableview.selectRow(at: indexpath, animated: true, scrollPosition: .bottom)
}
}
I'm currently trying to customize the UIPageControl so it will fit my needs. However I seem to be having a problem when implementing some logic in the draw
What I want to do is to be able to user IBInspectable variables to draw out the UIPageControl however those seem to still be nil when the draw method is being called and when I try to implement my logic in the awakeFromNib for instance it won't work.
What I did so far is the following
class BoardingPager: UIPageControl {
#IBInspectable var size: CGSize!
#IBInspectable var borderColor: UIColor!
#IBInspectable var borderWidth: CGFloat! = 1
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.pageIndicatorTintColor = UIColor.clear
}
override func draw(_ rect: CGRect) {
super.draw(rect)
setDots()
}
func setDots() {
for i in (0..<self.numberOfPages) {
let dot = self.subviews[i]
if size != nil {
let dotFrame = CGRect(x: size.width/2, y: size.height/2, width: size.width, height: size.height)
dot.frame = dotFrame
}
if i != self.currentPage {
dot.layer.cornerRadius = dot.frame.size.height / 2
dot.layer.borderColor = borderColor.cgColor
dot.layer.borderWidth = borderWidth
}
}
}
}
Another problem I'm facing is that I want to remove/add a border when the current page changes.
I'm hoping someone will be able to help me out
I am trying to make an NSScrollView which displays document pages. I have added a CALayer-backed NSView as the NSScrollView's documentView, and then I have added a CALayer sublayer to the documentView. When I zoom the NSScollview, the documentView correctly zooms in and out. However, the sublayers of the documentView do not scale proportionally with their containing documentView. If I do not set an autoresizingMask on the sublayers of the documentView layer, the sublayers simply fly off the screen when the documentView is zoomed. If I use the LayerWidthSizable/LayerHeightSizable options, the sublayers get way larger or smaller than they should be in reference to the documentView superlayer. Here is the code I have so far:
Here is the NSScrollview:
class LayerScrollView: NSScrollView {
var containerLayer: ContainerLayer!
override func awakeFromNib() {
documentView = ContainerLayer(frame: frame)
}
}
Here is the ContainerLayer (the documentView CALayer):
class ContainerLayer: NSView {
let documentLayer: DocumentLayer = DocumentLayer()
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
autoresizesSubviews = true
wantsLayer = true
layer = CATiledLayer()
layer?.delegate = self
layer?.backgroundColor = NSColor.blueColor().CGColor
layer?.masksToBounds = true
documentLayer.frame = CGRect(x: frame.width / 4.0, y: frame.height / 4.0, width: frame.width / 2.0, height: frame.height / 2.0)
documentLayer.delegate = documentLayer
layer?.addSublayer(documentLayer)
documentLayer.autoresizingMask = CAAutoresizingMask.LayerWidthSizable | CAAutoresizingMask.LayerHeightSizable
documentLayer.setNeedsDisplay()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
CGContextSetFillColorWithColor(ctx, NSColor.redColor().CGColor)
CGContextFillRect(ctx, layer.bounds)
}
}
And finally, here is the DocumentLayer (the sublayers contained in the DocumentLayer):
class DocumentLayer: CALayer {
override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
CGContextSetFillColorWithColor(ctx, NSColor.redColor().CGColor)
CGContextFillRect(ctx, layer.bounds)
}
}
Here is a picture to illustrate the problem and my desired results:
The blue rectangle is the ContainerLayer, the red rectangle is the DocumentLayer.
I have looked through numerous tutorials and the documents and come up empty. It seems like this should be super-easy to do. What am I missing here?
UPDATE: SOLVED IT
Here is the solution, found after more hours of digging through documentation and other stuff (also, thanks to #mahaltertin for suggesting that I use borderWidth to help debug):
The LayerScrollView:
class LayerScrollView: NSScrollView {
var containerLayer: ContainerLayer!
override func awakeFromNib() {
containerLayer = ContainerLayer(frame: frame)
documentView = containerLayer
}
}
The ContainerLayer:
class ContainerLayer: NSView {
let documentLayer: DocumentLayer = DocumentLayer()
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
wantsLayer = true
layer = CALayer()
layer?.layoutManager = CAConstraintLayoutManager.layoutManager()
layer?.delegate = self
layer?.backgroundColor = NSColor.blueColor().CGColor
documentLayer.delegate = documentLayer
documentLayer.name = "documentLayer"
documentLayer.borderWidth = 1.0
documentLayer.backgroundColor = NSColor.redColor().CGColor
documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.Width, relativeTo: "superlayer", attribute: CAConstraintAttribute.Width, scale: 0.5, offset: 0.0))
documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.Height, relativeTo: "superlayer", attribute: CAConstraintAttribute.Height, scale: 0.5, offset: 0.0))
documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.MidX, relativeTo: "superlayer", attribute: CAConstraintAttribute.MidX, scale: 1.0, offset: 0.0))
documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.MidY, relativeTo: "superlayer", attribute: CAConstraintAttribute.MidY, scale: 1.0, offset: 0.0))
layer?.addSublayer(documentLayer)
layer?.setNeedsDisplay()
documentLayer.setNeedsDisplay()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And the DocumentLayer:
class DocumentLayer: CALayer {
override func actionForKey(event: String!) -> CAAction! {
return nil
}
}
The solution is to use constraints. The override of the actionForKey method in the DocumentLayer is to prevent the DocumentLayer from animating while it zooms. The constraints defined in the ContainerLayer specify that the DocumentLayer should be half the width/height of the ContainerLayer, and that the middle of both layers should be the same. Zooming now keeps the proportions correct.