How to replicate the design and action of a UIButton selection from this app? - ios

I currently use an app and I'm trying to replicate an action that occurs in the app.
Here's an example:
As you can see, when my finger is on the selected "UIButton" (at least that's what I think they are), the background image is highlighted. Without releasing my finger, scrolling through the different options highlight that specific UIButton.
I believe each one is a UIButton with no text label, and they've just added a UILabel for each UIButton.
I've done something similar as soon:
However, what I can't figure out is how they managed to make the background highlighted between each selection.
It looks like a combination of a UIControlEvents.TouchDown and UIControlEvents.TouchUpInside.
I've coded up something similar:
cameraButton.addTarget(self, action: #selector(AddClothesViewController.selectCameraOptions(_:)), forControlEvents: .TouchUpInside)
cameraButton.addTarget(self, action: #selector(AddClothesViewController.selectCameraOptionsHighlighted(_:)), forControlEvents: .TouchDown)
func selectCameraOptionsHighlighted(button: UIButton)
{
if button == cameraButton
{
print("GO HERE")
cameraButton.setImage(UIImage(named: "highlighted background"), forState: .Normal)
}
}
func selectCameraOptions(button: UIButton)
{
if button == cameraButton
{
// DO STUFF
}
}
However this approach does not work, because if I perform a .TouchDown, somehow the camera options disappear. If I just add a target for .TouchUpInside, that obviously doesn't work neither because it's only highlighted once I release my finger, and by that time, it would have already taken me to a different view, i.e. Camera or Photo Library.
What's the best approach for how to achieve this?
Thanks.

Related

How to overlay a UIButton (close) button on top of a UIWebView?

I have been looking and trying to solve an issue I have with a UIWebView for iOS.
I have a very small app which just loads my website the source code lives at https://github.com/pjuu/iotter.
On the site when a user clicks on a posted image it load the image URL in to the web view. This leaves them stranded and unable to get back to other parts of the site.
What I want to do is display an (x) button in the top right hand corner (no matter what screen rotation is in use) and have perform the back operation in the browser.
I know this question isn't a code issue as I'm fine to sort of the Swift code part of it. I just could not for the life of me work out how to add the button programmatically and have it display over the webview.
The only tutorials I could find involved creating a tool bar to perform these type of actions which I think would take up to much space when looking at an image.
The question is very simple:
Is this possible?
If so how would I go about creating the button?
I am going to check the request.path parameter with regex to only show it if it matches an image URL.
Apologies if this isn't fitting enough for SO. I am happy to move the question or post on another site.
Thank you for any help in advance. I am not a mobile developer so using XCode and the storyboard stuff is a world away from vim for me.
REFERENCE:
1) CGRectMake: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGGeometry/#//apple_ref/c/func/CGRectMake
2) UIWebViewDelegate:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWebViewDelegate_Protocol/#//apple_ref/occ/intfm/UIWebViewDelegate/webView:shouldStartLoadWithRequest:navigationType:
SOLUTION:
1) Create the "X" UIButton:
// Define the UIButton
let button = UIButton(type: UIButtonType.System) as UIButton
let image = UIImage(named: "name") as UIImage?
button.frame = CGRectMake(self.view.frame.width - 60, 30, 35, 35)
button.setTitle("Test Button", forState: UIControlState.Normal)
button.setImage(image, forState: .Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
//Hide the UIButton
button.hidden = true
2) Detect when an imageUrl is opened and make the "X" UIButton appear:
// This function is triggered every time a request is made:
optional func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let string = request.URL
if string.rangeOfString(".png") || string.rangeOfString(".jpg")|| string.rangeOfString(".jpeg"){
// code to make cross appear
// for example:
self.button.hidden = false
}
}
3) Finally, you will need to create the method that closes the page when the "X" UIButton is tapped:
func btnPressed(sender: UIButton!) {
if(webview.canGoBack) {
//Go back in webview history
webview.goBack()
} else {
//Pop view controller to preview view controller
self.navigationController?.popViewControllerAnimated(true)
}
}
N.B.:
In the lines:
let image = UIImage(named: "cross.png") as UIImage?
button.setImage(image, forState: .Normal)
We are setting the UIButton to be the image of a cross.
You need to add a cross image to your XCode project and set the "cross.png" String to the exact name of your image: "nameOfImage.fileType".
Simple one line solution.
Create a desired "close" button in storyboard, or programmatically.
Then, just send your WebView to the back of all current views displayed in your view controller:
view.sendSubviewToBack(yourWebView)
Now, all views (including any buttons you create) will be displayed on top if it. Just be sure to add this line after all of your desired views are created.

Weird UIButton behaviour in Today Widget

I added a today extension to my app. I edited the vanilla widget a little bit and made it look like this:
Please notice the UIButton titled "Went 1st". When it gets pressed (touched up inside to be exact), it triggers this action:
#IBAction func coinChanged(sender: UIButton) {
if sender.titleLabel?.text == "Went 1st" {
coin = true
sender.titleLabel!.text = "Went 2nd"
} else {
coin = false
sender.titleLabel!.text = "Went 1st"
}
}
It basically alters between two states, changing its title and a variable accordingly.
Here is the problem though - when I press it, it indeed changes its title, but immediately changes it back, ending up on the same title as it had initially. My first thought was the action gets called twice after a press, but when I checked with print I found out it gets called only once. Sometimes the prints didn't even show up in console, but that's a different story.
So, that's one problem. There's one more, though - when I press the button, the whole widget gets misplaced. To know what I mean, look at the first picture (that's the widget before any presses) and now on this one (after the button gets pressed):
You can see that the borders are now on the very edge of TodayView. For reference, here are the constraints of the first segmented control:
Edit: Here are the constraints for "Went 1st/2nd" button:
Edit 2: Be sure to tell me what's wrong if you downvote, so I can avoid making the same mistakes next time
The problem is you should not be setting the button text like that. The title label is primarily used to set text size, font, color, etc. To set the title use something like this:
sender.setTitle("Button Title", forState: UIControlState.Normal)
So the new ib action should look like:
#IBAction func coinChanged(sender: UIButton) {
if sender.titleLabel?.text == "Went 1st" {
coin = true
sender.setTitle("Went 2nd", forState: UIControlState.Normal)
} else {
coin = false
sender.setTitle("Went 1st", forState: UIControlState.Normal)
}
}

Touch Up Inside not working properly

I have an app with some buttons, when those buttons are pressed the image on them should change. I assume that the TouchUpInside runs when you tap and remove the finger while still holding inside the area of the element, however it only works rarely and I'm not sure why.
The reason I use TouchUpInside instead of TouchDown is because I want the user to be able to cancel the action.
I'm sorry if I've misunderstood anything about those events and if this has already been asked. I couldn't find an answer to my problem searching the web.
//The IBAction is set to trigger on TouchUpInside
#IBAction func action11(sender: UIButton) {
setTile(sender)
}
func setTile(sender: UIButton) {
if turn {
print("O's turn")
sender.setImage(xTile, forState: .Normal)
turn = false
}
}
EDIT: Added the necessary code
There are some properties of UIButtons which you can use to achieve what you want.
You can use Default and selected state of uibutton to set two different images.
In XIB select state "Default" and assign default image to that state again select state to "Selected" and assign image which you want after button section.
and add following line in button selection method.
-(IBAction)buttonTapped:(UIButton *)sender{
sender.selected = !sender.selected;
}
Your understanding is correct, you need to use touchUpInside.
I assume you are trying to create a button that has a toggle function. On one touch you want the button to have the value Say "X" and when touched again the button has a value "O".
Take a look at this code below, this should do the job.
class ViewController: UIViewController {
var isButtonPressed = false{
// Adding a Property Observer, that reacts to changes in button state
didSet{
if isButtonPressed{
// Set the Value to X.
}else{
// Set the Value to O.
}
}
}
#IBAction func changeButtonValue(sender: UIButton) {
// Toggle the button value.
isButtonPressed = !isButtonPressed
}
}
If you don't set turn=true after the first time, this code is executed it will be executed only one.
if turn {
print("O's turn")
sender.setImage(xTile, forState: .Normal)
turn = false
}
Check if the button frame is large enough to get finger touch.
Apple says at least 35x35 pixel.

Is TouchUpOutside the correct control event for events outside of an element?

I'm three days new into swift development, even Xcode for that matter. I've created a UIView called EncounterMenu that appears over my app when i click the menu button. My goal is to have the EncounterMenu UIView close when I do anything outside of it.. This is what I have so far
override func viewDidLoad() {
super.viewDidLoad()
//create a button that spans the width & height of the encounter menu
let btnEncounterMenu = UIButton(type: UIButtonType.System) as UIButton
btnEncounterMenu.frame = CGRectMake(0, 0, EncounterMenu.frame.width, EncounterMenu.frame.height)
//(this is whats not working) If user clicks outside of button, call function "closeEncounter:"
btnEncounterMenu.addTarget(self, action: "closeEncounter:", forControlEvents: .TouchUpOutside)
//Add the button to the EncounterMenu
EncounterMenu.addSubview(btnEncounterMenu)
}
func closeEncounter(sender: UIButton!) {
EncounterMenu.hidden = true;
}
I tried changing it to TouchUpInside and it worked when i clicked inside the EncounterMenu UIView, so I figured it should be as easy as TouchUpOutside?
Any direction to what I'm doing wrong or how I can accomplish what I'm trying to do?
Touch up outside will not work because you need to be pressing on the menu first, then drag your finger outside for that to register. instead, the easiest option for you to do is to create a button that is the entire size of your view, and on that buttons touch down event, fire off close menu. just make sure that this big button is at the very back of the view, and everything else is built on top of it.

What's the simplest way to receive tap events on a disabled UIButton?

I have a UIButton on a form, and want to put it in a disabled state when the form is incomplete. However, I still want to be able to detect if a user attempts to press the button even in its disabled state so that the interface can let the user know that certain required fields on the form are not filled-in yet (and perhaps scroll to that field and point it out, etc.).
There doesn't seem to be any straightforward way to do this. I tried simply attaching a UITapGestureRecognizer to the UIButton but it doesn't respond when the button is in a disabled state.
I'd like to avoid subclassing UIButton if possible, unless it's the only way.
Create a fallback button. Put it behind the main button. Set its background and text colors to [UIColor clearColor] to ensure it won't show up. (You can't just set its alpha to 0 because that makes it ignore touches.) In Interface Builder, the fallback button should be above the main button in the list of subviews, like this:
Give it the same frame as the main button. If you're using autolayout, select both the main and fallback buttons and create constraints to keep all four edges equal.
When the main button is disabled, touches will pass through to the fallback button. When the main button is enabled, it will catch all the touches and the fallback button won't receive any.
Connect the fallback button to an action so you can detect when it's tapped.
Based on #rob idea, I sub-class a UIButton, and add a transparent button before someone addSubview on this button.
This custom UIButton will save many time about adjusting the UI components on the storyboard.
Update 2018/08
It works well, and add some enhanced detail to this sub-class. I have used it for 2 years.
class TTButton : UIButton {
// MARK: -
private lazy var fakeButton : UIButton! = self.initFakeButton()
private func initFakeButton() -> UIButton {
let btn = UIButton(frame: self.frame)
btn.backgroundColor = UIColor.clearColor()
btn.addTarget(self, action: #selector(self.handleDisabledTouchEvent), forControlEvents: UIControlEvents.TouchUpInside)
return btn
}
// Respect this property for `fakeButton` and `self` buttons
override var isUserInteractionEnabled: Bool {
didSet {
self.fakeButton.isUserInteractionEnabled = isUserInteractionEnabled
}
}
override func layoutSubviews() {
super.layoutSubviews()
// NOTE: `fakeButton` and `self` has the same `superView`.
self.fakeButton.frame = self.frame
}
override func willMoveToSuperview(newSuperview: UIView?) {
//1. newSuperView add `fakeButton` first.
if (newSuperview != nil) {
newSuperview!.addSubview(self.fakeButton)
} else {
self.fakeButton.removeFromSuperview()
}
//2. Then, newSuperView add `self` second.
super.willMoveToSuperview(newSuperview)
}
#objc private func handleDisabledTouchEvent() {
//NSLog("handle disabled touch event. Enabled: \(self.enabled)")
self.sendActionsForControlEvents(.TouchUpInside)
}
}
You have a great misunderstanding of user experience.
If a button is disabled, it is meant to be non-interactable.
You can not click on a disabled button, that is why it is disabled.
If you want to warn users about something when that button is clicked (e.g. form not filled correctly or completely), you need to make that button enabled. And just warn users when they click on it, instead of proceeding further with app logic.
Or you can keep that button disabled until form criteria are met, but show what is wrong with the form using another way, like putting exclamation marks near text fields, changing text field colors to red, or something like that...
But never try to add gesture recognizers, or hidden fallback buttons to a disabled button.
Check those and let me know if you see a disabled button:
https://airbnb.com/signup_login
https://spotify.com/us/signup/
https://netflix.com/signup/regform
https://reddit.com/register/
https://twitter.com/signup
https://facebook.com/r.php
https://appleid.apple.com/account
https://accounts.google.com/SignUp
https://login.yahoo.com/account/create
https://signup.live.com/signup
All the proceed buttons on these websites are always enabled, and you get feedback about what is wrong when you try to continue.
And here is really good answer: https://ux.stackexchange.com/a/76306
Long story short: disabled UI elements meant to be not-interactable.
Trying to make them interactable while they are disabled is the same to making them enabled in the first place.
So, for your question's case, it is just a styling issue. Just try styling your button, instead of making it disabled/enabled.

Resources