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

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.

Related

iOS Refresh Accessibility Label

I have a button with accessibility label #"a". When the button is pressed, I have a callback that sets the accessibility label button.accessibilityLabel = #"b". I know this line of code runs. However, if I tap the button again, VoiceOver still reads a. Unfortunately, the code I'm working with is proprietary, so I can't share it directly.
However, in general, I would like to know what issues might cause VoiceOver to not recognize an update to a label.
THE BEST way to handle dynamic accessibility labels is to override the property functions on the views that are being focused (EX: on a UIButton). This allows TWO things. A: it's a lot easier to maintain than setting the property everywhere it can change. B: you can log information and see when the system is requesting that information, so you can better understand WHY things are happening. So even if it doesn't directly fix your issue, seeing WHEN the system requests your value, and logging that data, is inherently valuable.
Doing this in Objective C
#implementation YourUIButton
-(NSString*)accessibilityLabel {
if(someCondition) {
return #"a";
} else {
return #"b";
}
}
#end
In Swift
public class YourUIButton : UIButton
override public var accessibilityLabel: String? {
get {
if (someCondition) {
return "a"
} else {
return "b"
}
}
set {
NSException.raise(NSException("AccessibilityLabelException", "You should not set this accessibility label.", blah))
}
}
}
You could also use this logic JUST to debug, and allow setting and such.
There are a lot of potential issues here. Race conditions, which view is actually getting focus, is there some parent child relationship going on, etc. Overriding the property and adding logging statements to the above code will help you understand what view is actually getting the accessibility label requested and when. Super valuable information!
Use this while changing button text
UIAccessibility.post(notification: .layoutChanged, argument: yourButton)
Try to add UIAccessibilityTraitUpdatesFrequently to your buttons property accessibilityTraits
- (void)viewDidLoad {
myButton.accessibilityTraits |= UIAccessibilityTraitUpdatesFrequently
}
Also, when changing accessibilityLabel be sure that you're on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
myButton.accessibilityLabel = #"b";
});
You don't really need a way to refresh the voice over labels. Its done automatically. I have tried this and it works as expected.
class ViewController: UIViewController {
var tapCount = 0
var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
button = UIButton(type: .system)
button.setTitle("Hello", for: .normal)
button.frame = CGRect(x: 10, y: 10, width: 100, height: 50)
view.addSubview(button)
button.accessibilityLabel = "Hello Button"
button.accessibilityHint = "Tap here to start the action"
button.accessibilityIdentifier = "hello_button"
button.addTarget(self, action: #selector(buttonTap(sender:)), for: .touchUpInside)
}
#IBAction func buttonTap(sender:UIButton) {
tapCount = tapCount + 1
sender.accessibilityLabel = "Hello button, tapped \(tapCount) times"
}
}
What Voice oversays:
Hello Button-pause-Button-pause-Tap here to start the action
On button tap
Hello button, tapped one times
Another tap
Hello button, tapped two times

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

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.

Swift - navigation buttons show & hide

Running into a really weird issue and couldn't find how to fix it. I have tab bar controller based app with 3 view controllers (2 table view controllers and one regular vc), they are Profile, Orders, People. When I sign in, I land on profile and the buttons in the nav bar show properly. As soon as I go to People and come back, the buttons disappear and don't show anymore. Also vice versa is true (going from People to Profile).
This is the additional weird part: if I go from profile to orders, then back, it will show all buttons. Also if I go from profile to orders to people, people shows normally.
Here is the structure:
In all viewDidLoad and viewWillAppear, I add the following code:
let editButton = UIButton()
editButton.frame = CGRectMake(0, 0, 60, 35)
editButton.setTitle("Edit Profile ", forState: .Normal)
editButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
editButton.addTarget(self, action: Selector("editButtonPressed"), forControlEvents: .TouchUpInside)
let leftBarButton = UIBarButtonItem()
leftBarButton.customView = editButton
self.tabBarController?.navigationItem.leftBarButtonItem = leftBarButton
and in the viewWillDisappear, I add:
self.tabBarController?.navigationItem.leftBarButtonItem = nil
self.tabBarController?.navigationItem.rightBarButtonItem = nil
All except Orders table view controller doesn't have any of the above code. What is going on? I will be happy to provide more if needed, but this is really all there is to it as far as I understand.
You can try this:
1.Show:
self.navigationItem.rightBarButtonItem?.customView?.alpha = 1.0
2.hide:
self.navigationItem.rightBarButtonItem?.customView?.alpha = 0.0

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)
}
}

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.

Resources