Swift - Set label to be on top of other view items - ios

I'm creating a basic sketching app using swift 1.2 and Xcode 6.4. The feature I'm working on is adding text to the image for future manipulation.
So far I have the text the user enters adding to the drawing via label, but it is being placed (I assume) behind the UIImage that is to be drawn upon. This is preventing the associated tap gesture from being recognised. The label is also obscured when drawn over.
How do I place the label on top of everything else to prevent it being drawn over? The end goal is to allow the user to move the label.
If I was using my usual web stack I'd set the z-index of the element to a high value via CSS.
Edit: Using view.bringSubviewToFront(label) prevents the text from being drawn over, but does not allow the tap gesture to be picked up.
Heres my code:
func printText(){
println(textString)
var label = UILabel(frame: CGRectMake(5, 100, 200, 50 ))
label.textAlignment = NSTextAlignment.Center
label.text = textString;
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
label.addGestureRecognizer(tap)
self.view.addSubview(label);
}
func handleTap() {
print("tap working")
}
Any help much appreciated.

If you want z-index use:
label.layer.zPosition = 1;
Also you could play with bringToFront method on the parent view:
self.view.bringSubviewToFront(label);
You can check that function here :
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/
Also, you should add the following:
label.userInteractionEnabled = true
to enable touches.

Try using those in the parent view:
view.bringSubviewToFront(label)
view.sendSubviewToBack(imageView)
It all depends on what you can, or want to do.

You can call label to front by
self.view.bringSubviewToFront(label)
or
You can send image view to the back by
self.view.sendSubviewToBack(imageView)

Related

Why do my objects get placed and sized so perfectly when I add them to a UIKitView?

So I have the following UIKit elements I am adding to my UIViewController to control the simulation that appears. I expected to have to write a lot of placement code but instead everything appears perfectly no-mater what device... my question is why?
let menuButton = UIButton()
let statusLabel = UILabel()
let segmentedLabel = UISegmentedControl(items: ["None", "Glow", "Cloud"])
func initializeUI() {
//The menu button that opens up the options for the simulations
menuButton.layer.borderColor = UIColor.lightGray.cgColor
menuButton.layer.borderWidth = 1
menuButton.layer.cornerRadius = 5
menuButton.layer.backgroundColor = UIColor.darkGray.cgColor
menuButton.showsTouchWhenHighlighted = true
menuButton.imageView?.contentMode = UIViewContentMode.scaleAspectFit
menuButton.setImage(UIImage(named: "hamburger.png"), for: UIControlState.normal)
menuButton.addTarget(self, action: #selector(menuPress), for: UIControlEvents.touchDown)
view.addSubview(menuButton)
//Will display the status of the simulation
statusLabel.text = "Particle Simulation"
statusLabel.textColor = UIColor.darkGray
view.addSubview(statusLabel)
//Will display visual options
view.addSubview(segmentedLabel)
}
The size of each of the elements is perfect and they dont overlap. The label is in the bottom left corner the button is in the bottom right and the segmented view is in the top left corner (of my landscape app).
An additional question I have is if I wanted to start placing these objects programmatically how would I do so? The elements don't have an attribute position that I can work with and if I do something like menuButton.frame.size.height *= 20 that does not make the menu button super tall.
The reason this works is that all of your UI items in your example have an “intrinsic” content size so they are able to create their own frame. Add a standard UIView the same way and you will be out of luck. Also the views are creating their own constraints based on initial frames using Auto Resizing Masks(Also known as Springs and Struts).
Finally to place items you would set the frame directly. Just math. Good luck.
Super important to understand intrinsic content size. Some frame changes might require you to turn off automatic constraints but this is usually not the case but could be the problem with your button
UIView.translatesAutoresizingMaskIntoConstraints = false

Error when trying to click in a view created programmatically swift 3

I created a view programmatically, like a popup coming from the top with some text and images in there!
alert = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.width , height: 0))
alert?.frame.origin.y = (textLable?.frame.height)!
alert?.frame.size.height = (textLable?.frame.height)!
alert?.backgroundColor = self.arrayOptions.colorBackground
and then I'm trying to add a UITapGestureRecognizer to that view like this inside a setup func that is called in the init.
let tap = UITapGestureRecognizer(target: self, action: #selector(self.teste))
tap.numberOfTapsRequired = 1
alert?.addGestureRecognizer(tap)
im adding that view like this in a UitableViewController:
self.popView = PopupView(frame: CGRect(x: 0 , y: 0 , width:self.view.frame.width, height: 0), with: PopUpOptions.error, originY: 0,description:"blablabla")
self.view.addSubview(self.popView!)
But when I tap on the view nothing happens, but when I tap repeatedly over and over this error occurs:
<_UISystemGestureGateGestureRecognizer: 0x174186f50>: Gesture: Failed
to receive system gesture state notification before next touch
But I cant seem to find an answer for this, could anyone help me pls!
Thank you!
here is the GitHub link https://github.com/Coimbraa/AlertsPopup_framework for my framework
Took a look at your GitHub repo - Couple notes...
I see a lot of "!" in your code. Each one of those is a potential crash. For example, I had to make a number of edits just to get it to run at all (I don't have the image assets the code is expecting).
In PopUpView.swift change the init() func to this (just added the clipsToBounds line):
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
self.clipsToBounds = true
}
Now, when you call popView?.toggleStatus() you probably won't see anything.
Your PopupView "container" has a height of Zero. Without clipping its contents, you see the content, but you can't interact with it.
I changed your animation block in toggleStatus() to this:
UIView.animate(withDuration: 0.5, animations: {
if let sz = self.alert?.frame.size {
self.frame.size.height = sz.height
}
self.alert?.frame.origin.y = (self.startY)! + (self.status ? 0 : -((self.textLable?.frame.height)! + self.startY))
}) { (finished:Bool) in
// ...
and I could now tap into and edit the TextView, and tap elsewhere and get print("pressed") output to the debug console.
I did not dig further, so I don't know if/where you need to put additional code to reset the frame-height back to Zero (or maybe it gets hidden or removed, whatever).
Try this:
alert.isUserInteractionEnabled = true
Set this property to true for those views you want to tap on (where you add the tap gesture recognizer).
You are adding that Custom Alert View in Table View Controller.
First try what Tung Fam has suggested, if it doesn't work then,
Trying adding it on window like:
self.view.window.addSubview(self.popView!)
Also set the frame of the window to the popupView.
As I'm seeing you are setting target for TapGesture as self in
let tap = UITapGestureRecognizer(target: self, action: #selector(self.teste))
Here self is object of UIView (your alert view).
So according to this line your tapGesture listener must be implemented in alert? class. And you need to use custom protocol or custom delegate to get the action of tapGesture in other classes.

How do I create a new View (or subView) with the tap of a button in swift?

I am currently making a flashcard app, and I am trying to make a button that creates an entirely new View (or subView) for the user to edit. Should I use Container Views? Collection Views? I also want to have these set to only one View Controller, so I can save the "cards" in one "set". Please Help!!
Edit: How can I save these views in a "folder" so the user can look at them later. Is there an efficient way to do this so the app doesn't slow or stop.
Edit #2: Ok, so I'm kind of getting it... collection views. But how would I implement this into my because I am using tvOS. Any thoughts?
If you want to create a new UIView programmatically, it's actually quite simple. You could create a method like this:
func addNewView(to container: UIView) {
let newView = UIView()
container.addSubview(newView)
newView.backgroundColor = UIColor.blue
newView.frame = CGRect(x: 10, y: 50, width: 200, height: 250)
}
That would create a new view inside whichever container view you passed in with a blue background, 10pts from the left (x-axis), 50pts from the top (y-axis, think a normal cartesian coordinate system with the y-axis inverted), width of 200 and height of 250.
You could then call this method on the push of a button by handling a button tap with it's own method like this:
func buttonTapped(_ sender: UIButton) {
addNewView(to: self.view)
}
Obviously all the values for the frame I gave you were just for an example so you could visualize it in your head, you can edit those however you want, or make calculations based on the size of your device's screen. You can get the device's screen size by saying self.view.bounds

Disable scrolling in a UITextView while avoiding both bouncing to top and invisibility of text at the present location for ios

I've tried a lot of things but couldn't come up with a solution. Any words of thought can help me evaluate on this. I made a full view DrawBoard class which is inherited from a UITextView class.When the switch on the view controller is on the user can type and scroll, when it is off the user can draw on the Drawboard.
#IBAction func changeSwitch(sender: UISwitch) {
if sender.on{
drawBoard.setNeedsDisplay()
drawBoard.scrollEnabled = true
drawBoard.editable = true
drawBoard.selectable = true
drawBoard.switchBool = false
}else if !sender.on {
drawBoard.switchBool=true
let a:CGPoint = drawBoard.contentOffset
drawBoard.scrollEnabled = false
drawBoard.setContentOffset(a, animated: false)
drawBoard.editable = false
drawBoard.selectable=false
}
}
It updates the scrolling but scrollEnabled= false just saves the text that is in the first page range of the textview so that it scrolls to the top automatically and disable the scrolling there.Then when I do the setContentOffset the drawable view of the background is visible and it draws on the right place of the textview. However the text that should be on top of it is not visible. This only happens when the switch button is set to off while I am out of the first page's range.How do I also make the text at that range visible?
Sorry if it's a really easy question I'm new to programming and got stuck for a considerable amount of time for this.Thank you.
You have to update the content size of the UIScrollView.
Like this:
scrollView.contentSize = CGSize(width: 100, height: 100)
"Content Size" determines the size of scrollable content, if it's not large enough to cover new views, you won't be able to see it in your scroll view.

UIButton in Swift is not registering touches

I'm trying to create a UIButton using Swift. It compiles fine and I can see my button in the simulator, but when I click it, nothing happens. This is the code I am using:
let settings = UIButton()
settings.addTarget(self, action: "touchedSet:", forControlEvents: UIControlEvents.TouchUpInside)
settings.setTitle("Settings", forState: .Normal)
settings.frame = CGRectMake(0, 530, 150, 50)
scrollView.addSubview(settings)
In the same class, here is the function 'touchedSet':
func touchedSet(sender: UIButton!) {
println("You tapped the button")
}
I'm using the simulator as I don't have an iOS 8.0 device, could that be the problem?
Thanks!
I see it is an old question but I just faced very similar problem yesterday. My buttons were highlighted on touch but not firing the action.
I have two view controllers. One view covers the others view and button is on the top view.
eg.
Rootviewcontroller has back view
Topviewcontroller has top view
The button on top view does not call the action if I don't add the topviewcontroler as childviewcontroller of the rootviewcontroller. After adding the topviewcontroller as childviewcontroller it started to working.
So in short: just try to add the view controller of buttons superview as childviewcontroller to the parent views viewcontroller with the following method
func addChildViewController(_ childController: UIViewController)
Selectors are a struct that have to be created.
Do it this way...
Updated for Swift 4
settingsButton.addTarget(self, action: #selector(showSettings), for: .touchUpInside)
That should do it.
For anyone else doing manual view layout running into this issue, you might have defined your subview like such:
let settingsButton: UIButton = {
let button = UIButton(type: .system)
// do some more setup
button.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
return button
}()
The error lies with the fact that you are adding a target and passing self before it is actually available.
This can be fixed in two ways
Making the variable lazy and still adding the target in the initialization block:
lazy var button: UIButton = {
let button = UIButton(type: .system)
// since this variable is lazy, we are guaranteed that self is available
button.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
return button
}()
Adding the target after your parent view has been initialized:
init() {
self.settingsButton = .init(type: .system)
super.init(frame: .zero)
self.settingsButton.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
}
In the simulator, sometimes it doesn't recognise the selector. There is a bug it seems. I just changed the action name (selector), and it worked.
let buttonPuzzle:UIButton = UIButton(frame: CGRectMake(100, 400, 100, 50))
buttonPuzzle.backgroundColor = UIColor.greenColor()
buttonPuzzle.setTitle("Puzzle", forState: UIControlState.Normal)
buttonPuzzle.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
buttonPuzzle.tag = 22;
self.view.addSubview(buttonPuzzle)
Selector Function is Here:
func buttonAction(sender:UIButton!)
{
var btnsendtag:UIButton = sender
if btnsendtag.tag == 22 {
//println("Button tapped tag 22")
}
}
I have had this problem when parent view of my button has isUserInteractionEnabled == false. All subviews will have the same userInteraction as their parent view. So I have just added this line parentView.isUserInteractionEnabled = true and the problem disappeared. Hope this helps someone.
For those who are also stuck in the tutorial:
I ran in to the same problem, when I was following Apple's "Start Developing iOS Apps (Swift)". It turned out that I had overlooked these code lines in the tutorial:
override var intrinsicContentSize : CGSize {
return CGSize(width: 240, height: 44)
}
Adding them to my RatingControl fixed the problem.
I had a similar problem when i had a subViewController with buttons and tapHandlers to match, and I wanted to place this to a stack in a separate superViewController..
This cause none of the tapHandlers to to trigger, and the solution was to instead of only using addArrangedSubview(subViewController.view), I also used addChildViewController(subViewController) in the superview to allow the childViewController to continue to operate as a viewController and not just a view.
So you should use following line
settings.userInteractionEnabled = true
Old question, but thought I'd give some help to other looking for this issue. First off, I'm not entire sure this is correct so please correct me if I'm wrong.
But if you set the selector wrongly the application would crash when you press the button since it's subscribed to a selector which doesn't exist. Which means that your selector is working. My guess is that something else is blocking the touch event, assuming UIButton.enabled = true and UIButton.userInteractionEnabled = true. Usually you could check this by long pressing the button and see if it hits. If it does then you have a UIGestureRecognizer added somewhere which takes up the hits first. Could also be the UIScrollView which is delaying the touch input (there's a checkbox for this in the Inspector).
Hope this helps someone, and hope it's accurate.
I had the same issue when implementing UIButton and SearchBar on container view(UIView).
In my case, the cause was the constraint issue. Constraint to searchBar had issue and because of this, function set had never been called.
You can see if there's any from "Debug View Hierarchy" button. (constraint problem is shown as purple warning)
(env: iOS12, xcode10.1)
Interestingly enough, I just ran into the same issue on the very latest versions of iOS/Xcode (v12.4 / v10.3). Turns out the issue for me was a LayoutConstraint! No joke. I had a leading label with the uiSwitch to the right, and found that I needed to change the constraints between the two such that it wasn't a fixed constant value of 8 points (Label-8-Switch-0-|).
As soon as I changed this to a ">=" the Switch was able to change states between on/off. Laughably, it's almost like it wasn't able to change because it needs "room" (that apparently varies) to make the change with.
Not sure, but file it away as one of those "hummmm?" items in your memory.
One other item (that really shouldn't matter, frankly) is the valueChanged and tappedUpInside were both not firing (true on both an actual handset and on the simulators). Also, they were hooked up through a storyboard. But, this shouldn't matter as far as I know.
I had the same issue. The problem was the view had top constraint, but not left/right and height constraints. So, the view was shrinking to 1x1 and it was not passing the touch event to children.
By adding more constraints, the children now getting the touch event and working ...
let guide = view.safeAreaLayoutGuide
viewHeader.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
viewHeader.rightAnchor.constraint(equalTo: guide.rightAnchor).isActive = true
viewHeader.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
let heightConstraint = NSLayoutConstraint(item: viewHeader,
attribute: NSLayoutConstraint.Attribute.height,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 44)
viewHeader.addConstraints([heightConstraint])
Old question, but I also found myself stuck with an unresponding button. I simply changed my initialisation from
let button = UIButton(frame: .zero)
to
let button = UIButton(type: .system)
I can see only one problem: You have to set the action with Selector as following:
settings.addTarget(self, action: Selector("touchedSet:"), forControlEvents: UIControlEvents.TouchUpInside)
Don't forget to insert : if you need to pass parameters to the function.
You said within the same class. Make sure that the button code itself is in the viewDidLoad() and the action be outside of it but inside the class if you are working with a view.
The problem with that tutorial from Apple is the way they put the UIView (RatingControl) inside the StackView, which is incorrect. The UIView (RatingControl) should be outside of the StackView. So, the solution is:
1. Drag the UIView (RatingControl) outside of the StackView
2. Set the constraints for the new position of the RatingControl
- Leading margin to the Container equal zero
- Trailing margin to the Container equal zero
- Top margin to the StackView equal zero
With this new constraints, now the RatingControl will have the same with as the Container, no matter what is the device screen size, and always will be just behind of the StackView where the photo selector will appear.
I hope this help you guys.
Make sure you link you button with your IBOutlet instance variable in your code if you're using storyboard.
I've found similar example for Swift 2 and adapted for Swift 3. Tested it is working very well. I hope it helps.
// http://lab.dejaworks.com/ios-swift-3-playground-uibutton-action
// Trevor D. Beydag
import UIKit
import PlaygroundSupport
class Responder : NSObject {
func action() {
print("Button pressed!")
}
}
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
PlaygroundPage.current.liveView = containerView
let responder = Responder()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = UIColor.green
button.setTitle("TEST", for: .normal)
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
containerView.addSubview(button)
I had changed the height and width constraints to 0, for some reason I needed to do this so it showed correctly on a multipurpose view for one use case, the buttons were visible but the touch stopped working.
In my case, I had a transparent UIView above the UIButton in the view hierarchy, which is why when I was "clicking on the UIButton", I was actually clicking on the transparent UIView on top of it.
Sending the transparent UIView back using parentUIView.sendSubviewToBack(transparentUIView) worked for me, because after doing this the UIButton came on top of the view hierarchy. Click on the "Debug View Hierarchy" button in Xcode to check if the UIButton is on top or not.
If you're using Autolayout to build your UI, make sure to declare all your constraints in your view and parent views.
Some times we forget to set all of them and the system complains about it, drawing your UI but not responding correctly in your touch events.
In my case, i’ve forgot to set my bottom constraint in my view so i’m here to set this comment as a reminder.
When a view like a button is placed next to another view like an image view etc. especially if if the image view is on top of another view Xcode sometimes thinks that your button is underneath that image view even though it is not. Sometimes you need to clear all the constraints and reset all constraints in order for the button to work.

Resources