UIButton click function opens URL in blank screen - ios

I am learning iOS and Swift development, and I was able to get a button click to open a URL.
Within Xcode, I am running the app by clicking the play button.
When the app opens, and I click the button on the app, it takes me to the URL.
The problem is: the screen is blank.
The address bar does show the URL.
Here is a portion of the code that I wrote that opens the URL:
class ViewController: UIViewController {
private lazy var visitwebsitebutton: UIButton = {
let visitwebsitebutton = UIButton()
let dtgreen = UIColor(rgb: 0x12823b)
visitwebsitebutton.backgroundColor = .green
visitwebsitebutton.setTitle("Visit Website", for: .normal)
visitwebsitebutton.setTitleColor(.white, for: .normal)
visitwebsitebutton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
visitwebsitebutton.layer.cornerRadius = 20
visitwebsitebutton.addTarget(self, action: #selector(visitwebsitebuttonTapped), for: .touchUpInside)
return visitwebsitebutton
}()
#objc func visitwebsitebuttonTapped() {
if let yourURL = URL(string: "https://www.somesite.com") {
UIApplication.shared.open(yourURL, options: [:], completionHandler: nil)
}
}
}
Using the above will successfully allow the user (which is me at the moment) to click on the button, and open the URL in another window on the Xcode emulator. But the screen is blank.
My question is: does this blank screen only occur when running the app in Xcode? Will it work properly when I publish this app to the web?

Related

Swift - open URL from button click

I am learning Swift and iOS development, and I am just trying to figure out how to open an URL from a button click.
I found this answer: SwiftUI: How do I make a button open a URL in safari?
So I am trying to incorporate "Link" into my code below:
class ViewController: UIViewController {
private let visitwebsitebutton: UIButton = {
let visitwebsitebutton = UIButton()
visitwebsitebutton.backgroundColor = .gray
visitwebsitebutton.setTitle("Visit Website", for: .normal)
visitwebsitebutton.setTitleColor(.white, for: .normal)
visitwebsitebutton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
visitwebsitebutton.layer.cornerRadius = 20
visitwebsitebutton.Link("Some label", destination: URL(string: "https://www.mylink.com")!) // <-- link used here
return visitwebsitebutton
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(visitwebsitebutton)
}
}
Using Link above gives me an error that reads "Value of type 'UIButton' has no member 'Link'".
What am I doing wrong and how can I fix it?
Edit 1
I just tried this inside private let visitwebsitebutton:
visitwebsitebutton(action: {"www.redacted.com"})
But now I'm getting the below error:
Cannot call value of non-function type 'UIButton'
Edit 2
Within private let visitwebsitebutton, I attempted the following:
visitwebsitebutton.addTarget(self, action: "buttonClicked", for: UIControl.Event.touchUpInside)
Using the above, I am getting a few warning:
'self' refers to the method 'ViewController.self', which may be unexpected
Use 'ViewController.self' to silence this warning
No method declared with Objective-C selector 'buttonClicked'
Replace '"buttonClicked"' with 'Selector("buttonClicked")'
I tried to call the buttonClicked like this:
#objc func buttonClicked(sender:UIButton)
{
if(sender.tag == 5){
var abc = "argOne" //Do something for tag 5
}
print("hello")
}
And above, I am getting the below warning:
Initialization of variable 'abc' was never used; consider replacing with assignment to '_' or removing it
Replace 'var abc' with '_'
I just want to get the button to work.
This is how I solved the problem:
class ViewController: UIViewController {
private lazy var visitwebsitebutton: UIButton = {
let visitwebsitebutton = UIButton()
let mygreen = UIColor(rgb: 0x12823b)
visitwebsitebutton.backgroundColor = mygreen
visitwebsitebutton.setTitle("Visit Website", for: .normal)
visitwebsitebutton.setTitleColor(.white, for: .normal)
visitwebsitebutton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
visitwebsitebutton.layer.cornerRadius = 20
visitwebsitebutton.addTarget(self, action: #selector(visitwebsitebuttonTapped), for: .touchUpInside)
return visitwebsitebutton
}()
#objc func visitwebsitebuttonTapped() {
if let yourURL = URL(string: "https://www.somesite.com") {
UIApplication.shared.open(yourURL, options: [:], completionHandler: nil)
}
}
}
If anyone needs help with iOS mobile development with Swift, and you just want to be able to click on a button and have it take you to a site, look no further.

Swift 5 UIButton with image causing my apps freezing after open url

I have an application that needs to open third-party apps (i.e Whatsapp), it runs smoothly when I build this app using Xcode 10, but in Xcode 12, it's causing the freeze. Does anyone have to face this problem before?
My code was like this
declaring button
let buttonWA: UIButton = {
let v = UIButton()
v.imageView?.contentMode = .scaleAspectFit
v.setImage(UIImage(named: "ic_whatsapp"), for: .normal)
return v
}()
let buttonEmail: UIButton = {
let v = UIButton()
v.imageView?.contentMode = .scaleAspectFit
v.setImage(UIImage(named: "ic_email"), for: .normal)
return v
}()
Then I add some functions to be called each button
#objc func WaPressed() {
let no = "*************".urlwithPercentEscapes()
let urlWhatsApp = "whatsapp://send?phone=\(no)"
self.openUrl(urlString: urlWhatsApp)
}
#objc func EmailPressed() {
self.openUrl(urlString: "mailto:myEmail#gmail.com")
}
func openUrl(urlString: String) {
guard let url = URL(string: urlString) else {
return
}
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}
Everything running smoothly, and open third apps very well, but when I returning back to my apps, it goes freeze without telling me anything
I've tried change ".normal" options to other options like ".application" etc in the line
v.setImage(UIImage(named: "ic_whatsapp"), for: .normal)
and it makes my apps running well again, but I need this "for: .normal" state so bad.
Then I've tried to change
v.setImage(UIImage(named: "ic_whatsapp"), for: .normal)
to
v.setBackgroundImage(UIImage(named: "ic_whatsapp"), for: .normal)
and it works, but then the image scale is messy.
Could anybody help me to solve this?
After trying other options, I figure it out with add RenderingMode when I set Image in my button, the code looks like this:
For whatsapp button
v.setImage(UIImage(named: "ic_whatsapp").withRenderingMode(.alwaysOriginal), for: .normal)
For email button
v.setImage(UIImage(named: "ic_email").withRenderingMode(.automatic), for: .normal)
I don't understand why I can't set "automatic / alwaysOriginal" both. But this way works for me.
Thankyou

How to set only apple logo image to sign in with apple button

I am developing an app where i have provided three sign-in options like facebook, google and apple. and i need to set same designs for all three. my sign-in with apple button is rectangular and i want to know can i change design of that button and how. i need to make it circular with apple logo only and with not text. so how can i set apple logo image to ASAuthorizationAppleIDButton. i have read about apple designs on following link but nothing is mentioned about how to set image to button .
https://developer.apple.com/design/human-interface-guidelines/apple-pay/overview/buttons-and-marks/
You need to make your custom button as per Apple Human Interface Guideline. You have to follow their guidelines to avoid review rejections.
You can download apple design resource from here
To add custom button in app:
func appleCustomLoginButton() {
//Sign in with app is only available from iOS 13 onwards
if #available(iOS 13.0, *) {
let customAppleLoginBtn = UIButton()
customAppleLoginBtn.backgroundColor = UIColor.white
customAppleLoginBtn.setImage(UIImage(named: "appleLogo"), for: .normal)
customAppleLoginBtn.addTarget(self, action: #selector(actionHandleAppleSignin), for: .touchUpInside)
view.addSubview(customAppleLoginBtn)
// Setup Layout Constraints to be in the center of the screen
customAppleLoginBtn.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customAppleLoginBtn.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
customAppleLoginBtn.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
customAppleLoginBtn.widthAnchor.constraint(equalToConstant: 200),
customAppleLoginBtn.heightAnchor.constraint(equalToConstant: 40)
])
}
}
#objc func actionHandleAppleSignin() {
//do something when button is clicked
}
Its very simple. If you have any further query please feel free to ask, Thank you.
//1. setting apple logo
let appleLogo = UIImage(systemName: "appleLogo.png")!
//2. create a button
let button = UIButton()
//3. setImage with image name
button.setImage(appleLogo, for: .normal)
//4. Set image rounded.
button.layer.cornerRadius = button.frame.height/2
//5. Setting empty button text
button.titleLabel?.text = ""

UIButton not toggling between two images Swift 4.2

I am wanting to add sounds to my app. I have added a UIButton with two images, soundON and soundOFF.
When I call the sound settings in the app the first time, they toggle fine with each image.
However, when I return to the sound settings a second and subsequent time, it is like the soundOff images does not disappear when the soundOn image is displayed.
Odd as the code is so short and simple.
func soundButton() {
sounds = UIButton(frame : CGRect(x: 65, y: 70, width: 40, height:40))
sounds.setImage(UIImage(named : "soundON"), for : .normal)
sounds.setImage(UIImage(named : "soundOFF"), for : .selected)
sounds.showsTouchWhenHighlighted = true
sounds.addTarget(self, action: #selector(soundButtonTapped), for: .touchUpInside)
self.soundView.addSubview(sounds)
}
#objc func soundButtonTapped(_ sender: Any) {
sounds.isSelected.toggle()
isSoundOn.toggle()
}
I have added a video to show the issue as this will save a ton of typing.
http://www.reeflifeapps.com/soundError.mov
Any help is greatly appreciated.
update:
I had the button on a UIView that was hidden on startup of the puzzle. When the user pressed the "Sounds Settings" icon, the sound setting UIView was unhidden. I had the button on this func to unhide the sound settings. I moved it to viewDidLoad() and it fixed it.
I suggest you to define a boolean variable that keeps the current state of your button. Then, you should set current image of button according to the current state of the variable.
var isSoundOn = false
#objc func soundButtonTapped(_ sender: Any) {
isSoundOn.toggle()
if isSoundOn {
// your logic when sound on (set button selected image, action etc)
} else {
// logic when sound off (set button not selected image, action etc)
}
}
If you still want to use soundButton.isSelected as your boolean variable,do not define images for different states in soundButton.setImage(yourImage, for: .selected) and soundButton.setImage(yourImage, for: .normal) and define them as follows:
soundButton.setImage(soundButton.isSelected ? soundOnImage : soundOffImage, for: .normal)
One of those two approaches above can be used.
UPDATE:
As Lloyd Kaijzer stated, isSoundOn = !isSoundOn updated as isSoundOn.toggle()

Change the button titles on SLComposeServiceViewController?

Is there a way to change the button titles on the SLComposeServiceViewController? I tried to change the bar button items on the navigation item, but those aren't the right buttons.
Simply accessing from navigationController!.navigationBar does the charm. The following should help.
self.navigationController!.navigationBar.topItem!.rightBarButtonItem!.title = "Save"
I just found a way to do it:
class CustomServiceViewController: SLComposeServiceViewController {
override func viewDidLoad() {
let navigationBar = view.subviews.first?.subviews?.last? as? UINavigationBar
let postButton = navigationBar?.subviews.last? as? UIButton
let cancelButton = navigationBar?.subviews.last? as? UIButton
postButton?.setTitle("Done", forState: .Normal)
}
}
Be warned - it's a fragile solution, based on undocumented internals of SLComposeServiceViewController
The answer by Kasztan no longer works with the latest iOS; here is the latest fragile solution..
class CustomServiceViewController: SLComposeServiceViewController {
override func viewDidLoad() {
let navigationBar = view.subviews.last?.subviews?.last? as? UINavigationBar
let postButton = navigationBar?.subviews[3] as? UIButton
postButton?.setTitle("Done", forState: .Normal)
}
}
EDIT #3: Solution working on iOS 9 and iOS 10 beta
The previous approach stopped working with iOS 9, but the following seems to work again (tested on iOS 9 and 10 beta 2):
1) First, you need to add a UIFont class extension to check if a button font is bold (this, because the Post button is always bold); here's how.
2) Then, in viewDidAppear:, we need the following code (an updated version of the code I wrote in Edit 2):
if let navigationBar = self.navigationController?.navigationBar {
// First, let's set backgroundColor and tintColor for our share extension bar buttons
navigationBar.backgroundColor = UIColor.darkGrayColor()
navigationBar.tintColor = UIColor.whiteColor()
if let navBarSubviews = navigationBar.subviews as? [UIView] {
for eachView in navBarSubviews {
if let navBarButton = eachView as? UIButton {
// Second, let's set our custom titles for both buttons (Cancel and Post); checking for the title wouldn't work for localized devices, so we check if the button is bold (Post) or not (Cancel) via the UIFont class extension above.
let buttonFont : UIFont? = navBarButton.titleLabel?.font
if buttonFont?.isBold == true {
navBarButton.setTitle("Save", forState: .Normal)
} else {
navBarButton.setTitle("Cancel", forState: .Normal)
}
}
}
}
}
Of course, this works now, but it will probably break again in the future...
EDIT #2: I made it work on a device with iOS 8.4 :)
Turns out I was wrong, after spending an unreasonable amount of time on this I've been able to both change the color of the buttons and their text.
Here's my code, that needs to be put inside ViedDidAppear() (if you place it in viewDidLoad() it won't work!):
if let navigationBar = self.navigationController?.navigationBar {
// First, let's set backgroundColor and tintColor for our share extension bar buttons
navigationBar.backgroundColor = UIColor.darkGrayColor()
navigationBar.tintColor = UIColor.whiteColor()
if let navBarSubviews = navigationBar.subviews as? [UIView] {
for eachView in navBarSubviews {
if let navBarButton = eachView as? UIButton {
// Second, let's set our custom titles for both buttons (Cancel and Post); checking for the title wouldn't work on localized devices, so we check if the current button is emphasized (Post) or not (Cancel) via an UIFontDescriptor.
let fontDescriptor : UIFontDescriptor? = navBarButton.titleLabel?.font.fontDescriptor()
if let descriptor = fontDescriptor {
let fontAttributes : NSDictionary = descriptor.fontAttributes()
var buttonFontIsEmphasized : Bool? = fontAttributes["NSCTFontUIUsageAttribute"]?.isEqualToString("CTFontEmphasizedUsage")
if buttonFontIsEmphasized == true {
navBarButton.setTitle("Save", forState: .Normal)
} else {
navBarButton.setTitle("Cancel", forState: .Normal)
}
}
}
}
}
}
Still, I'm not sure this should be done on a shipping app nor it would pass App Review (it should, though, because it doesn't mess with private APIs).
Also, it should be noted that this could break anytime, even though it shouldn't be as easily breakable as the previous solutions (it iterates through the subviews and attempts downcasting them, so a small change in the view hierarchy shouldn't render it useless); my expectations is that, even if in the future it stops working, it shouldn't crash the Share Extension.
Original answer
I believe what you (and I) want to do is not possible anymore, possibly by design. Here's why:
Inspired by #Kasztan and #Paito answers, I tried this in viewDidLoad() of my ShareViewController:
for eachView in view.subviews {
println("1")
for eachSubView in eachView.subviews {
println("2")
if let navigationBarView = eachSubView as? UINavigationBar {
println("3")
for eachNavBarSubView in navigationBarView.subviews {
println("4")
if let navBarButton = eachNavBarSubView as? UIButton {
println("5")
println(navBarButton.titleForState(.Normal))
navBarButton.setTitleColor(UIColor.redColor(), forState: .Normal)
navBarButton.setTitle("My text", forState: .Normal)
navBarButton.tintColor = UIColor.redColor()
navBarButton.setNeedsLayout()
navBarButton.layoutIfNeeded()
}
}
}
}
}
Not that I believe something like this should ship in an app, but as a proof of concept this should have worked and, in theory, should be a bit less breakable with future releases of the OS.
Except, it didn't work for me on iOS 8.4: I see all the checkpoint messages logged, from 1 to 5, some of them multiple times (as it should be, since the code tries every possible subview).
The "5" message is logged twice, which makes sense since it means that it successfully downcast both the buttons, Cancel and Post, but not the text nor the color is changed from the default.
My conclusion is that something in Apple's code prevents us to change the appearance of those buttons.
Of course, if anyone finds a solution, I'd be glad to downvote my own answer (if it can be done, I'm note sure) ;)
EDIT #1: One last check, I logged the button title too, after the buttons downcast (5), and yes, I got Optional("Cancel") and Optional("Post") in the console, so this solution gets the right buttons, but they can't be edited.

Resources