Swift 2.3 Button to open URL failing with error unrecognised selector - objective-c

I have created a button to open url links and am failing with an unrecognised selector error. I would normally set the add target to be self just through how i have read online, but for this particular instance i get the error:
cannot convert value of type NSOBJECT ->() -> infoViewcontroller.infoview to expected argument type AnyObject.
So to get around this and what Xcode recommended was to set the target as NSOBJECT.self. However this no longer got an error but crashes upon the click of the button and returns the reason of: [NSObject displayWebLink]: unrecognized selector sent to class 0x106011e58. So i'm just wondering what the correct way of doing this would be and why? Below is my code.
class ActivityInfoView: UICollectionViewCell {
var activity: ActivitysEvents? {
didSet {
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var textView: UITextView = {
let tv = UITextView()
tv.userInteractionEnabled = false
return tv
}()
let dividerLineView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.lightGrayColor()
return view
}()
let urlLinkButton: UIButton = {
let button = UIButton()
button.backgroundColor = UIColor.redColor()
button.titleLabel?.font = UIFont.systemFontOfSize(14)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
// button.addTarget(self, action: #selector(displayWebLink), forControlEvents: .TouchUpInside)
button.addTarget(NSObject.self, action: #selector(displayWebLink), forControlEvents: .TouchUpInside)
return button
}()
func displayWebLink() {
print("abcdefghijklmnop")
if let urlLink = activity?.url {
// UIApplication.sharedApplication().openURL(NSURL(string: urlLink)!)
UIApplication.sharedApplication().openURL(NSURL(string: urlLink)!, options: [:], completionHandler: nil)
print("dhudududuhdu")
}
}
`

That's not a very helpful error message. The compiler is trying to verify that the correct object defines a method with the name displayWebLink and it seems to be using the closure type as the context where it's searching.
Try telling it where to find the method:
button.addTarget(self, action: #selector(ActivityInfoView.displayWebLink), forControlEvents: .TouchUpInside)

I fixed this issue by changing the button from 'let' to 'lazy var' as below:
lazy var urlLinkButton: UIButton = {
let button = UIButton()
button.backgroundColor = UIColor.redColor()
button.titleLabel?.font = UIFont.systemFontOfSize(14)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.addTarget(self, action: #selector(displayWebLink), forControlEvents: .TouchUpInside)
//button.addTarget(self(), action: #selector(ActivityInfoView.displayWebLink), forControlEvents: .TouchUpInside)
return button
}()

Related

How to use AVPlayer and Image in UITableViewCell depending upon the response from my API

Functionality : I need to implement a tableview where the cells will have either an image or a video depending upon the category in my API.
Whats happening : The images and video are displayed well but when a cell with video is displayed it replaces all the cell with AVPlayer, the cells with the images too.
Code : -
(in cellForRowAtIndexPath - )
if (![dict[#"video_360x290"] isEqualToString:#""]) {
AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:dict[#"video_360x290"]]];
AVPlayer *playVideo = [[AVPlayer alloc] initWithPlayerItem:playerItem];
cell.playerViewController = [[AVPlayerViewController alloc] init];
cell.playerViewController.player = playVideo;
cell.playerViewController.player.volume = 10;
cell.playerViewController.view.frame = CGRectMake(0, 0, cell.vPost.frame.size.width, cell.vPost.frame.size.height);
[cell.vPost addSubview:cell.playerViewController.view];
[playVideo play];
}
else{
[cell.iPost sd_setImageWithURL:dict[#"image"] placeholderImage:[UIImage imageNamed:#"imageNotAvailable"] options:SDWebImageHighPriority];
}
What i Need : I need that cell with images displays images and cell with video displays video
Thanks in advance.
Let TableViewCell Class name is ImageVideoCell
Create THREE cells from Storyboard in TableView, 1 for Image and 2 for Video and change cell identifier. For Image Cell change Identifier to "ImageCell" and for Video Cell change Identifier to "VideoCell1" and "VideoCell2" add same tableViewCell Class on all cells like here ImageVideoCell.
And in Controller
class ViewController: UIViewController {
// MARK: - Outlets
#IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var imgVideos = [ImgVideo?]()
var player : AVPlayer?
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// API call to get Images and videos in TableView
tableView.dataSource = self
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
if player?.isPlaying == true {
self.player?.pause()
self.player = nil
}
}
// MARK: - Action
#IBAction func play_pauseButtonPressed(_ sender: UIButton) {
guard let video = imgVideos[sender.tag] else { return } // get video URl here
guard let cell = tableView.cellForRow(at: IndexPath(row: sender.tag, section: 0)) as? ImageVideoCell else { return }
// if Video is already Playing And User want to Pause it then
if player?.isPlaying ?? false && video.isSelected {
player?.pause()
sender.setImage(#imageLiteral(resourceName: "play-button"), for: .normal)
}
// if the Video is pause and User want to Play it again
else if player?.isPlaying ?? true == false && video.isSelected {
player?.play()
sender.setImage(#imageLiteral(resourceName: "media-pause"), for: .normal)
}
// Play any other Video
else {
for (index, video) in audios.enumerated() {
if video?.isSelected ?? false {
video?.isSelected = false
tableView.beginUpdates()
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
tableView.endUpdates()
}
}
video.isSelected = true
tableView.beginUpdates()
tableView.reloadRows(at: [IndexPath(row: sender.tag, section: 0)], with: .none)
tableView.endUpdates()
guard let cell = tableView.cellForRow(at: IndexPath(row: sender.tag, section: 0)) as? ImageVideoCell else { return }
cell.play_pauseButton.setImage(#imageLiteral(resourceName: "media-pause"), for: .normal)
play(videoUrl: vedio.url ?? "") // Play is a function for playing Videos
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ImageVideoCell()
if Image {
cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell", for: indexPath) as! ImageVideoCell
// Display Img
}else {
if video?.isSelected ?? false {
cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell1", for: indexPath) as! ImageVideoCell
}else {
cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell2", for: indexPath) as! ImageVideoCell
}
}
cell.play_pauseButton.tag = indexPath.row
cell.play_pauseButton.setImage(audio?.isSelected ?? false ? #imageLiteral(resourceName: "media-pause") : #imageLiteral(resourceName: "play-button"), for: .normal)
return cell
}
}
// MARK: - AVPlayer
extension AVPlayer {
var isPlaying: Bool {
return ((rate != 0) && (error == nil))
}
}

ios 10 Snapshotting a view that has not been rendered results in an empty snapshot

this question ask again but i dont find for ios 10
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera)
{
self.imagePicker.delegate = self
self.imagePicker.sourceType = UIImagePickerControllerSourceType.camera;
self.imagePicker.allowsEditing = false
self.imagePicker.cameraCaptureMode = .photo
//self.imagePicker.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
self.present(self.imagePicker, animated: true, completion: nil)
self.imagePicked.isUserInteractionEnabled = true
}
else
{
print("No Camera")
}
Snapshotting a view that has not been rendered results in an empty
snapshot.Ensure your view has been rendered at least once before
snapshotting or snapshot after screen updates.
when i rotate the camera and take a shot than this error occurs.
Self Solution Working for me like charm :-) hope its helpful for all
DispatchQueue.global(qos: .userInitiated).async
{
self.present(self.imagePicker, animated: true, completion: nil)
}
I got the error
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes...
Using DispatchQueue.main.async instead works for me.
This behavior is not limited to UIImagePickerController. Below is an example of a UIViewController which presents another UIViewController modally. In the second UIViewController, Safari is launched to present a URL, thus triggering the same error message, "Cannot snapshot view (>) with afterScreenUpdates:NO, because the view is not in a window. Use afterScreenUpdates:YES."
I haven't yet found any way of suppressing the message, but in my app it does no harm. I think what's going on here is that some Apple code is taking a snapshot of the app's view hierarchy, but the keyboard (which is owned by a separate UIWIndow) has not been rendered before the snapshot is taken.
/* Generates the error message:
Cannot snapshot view (<UIKeyboardImpl: 0x7f82ded12ea0; frame = (0 0; 414 271); layer = <CALayer: 0x610000035e20>>) with afterScreenUpdates:NO, because the view is not in a window. Use afterScreenUpdates:YES.
... after the "Now Tap Me, For Glory!" label is clicked
*/
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let input = UITextField()
input.placeholder = "Tap here first -- to bring up keyboard"
input.frame = CGRect(x: 10, y: 50, width: 300, height: 20)
view.addSubview(input)
let button = UIButton()
button.setTitleColor(UIColor.blue, for: .normal)
button.setTitle("Then tap here", for: .normal)
button.addTarget(self,
action: #selector(buttonPushed),
for: .touchUpInside)
button.frame = CGRect(x: 10, y: 80, width: 200, height: 20)
view.addSubview(button)
}
func buttonPushed() {
let modalVC = ModalViewController()
present(modalVC, animated: true, completion: nil)
}
}
class ModalViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let label = UILabel()
label.text = "Now Tap Me, For Glory!"
label.frame = CGRect(x: 10, y: 50, width: 300, height: 20)
view.addSubview(label)
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(labelTapped)))
}
func labelTapped() {
UIApplication.shared.open(URL(string: "http://planetbeagle.com")!, options: [:], completionHandler: { _ in
self.dismiss(animated: true, completion: nil) })
}
}

MKAnnotationView not detecting if button or title touched

Hello my problem is that this code:
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control is UIButton {
if (control as! UIButton).buttonType == .DetailDisclosure{removeAnnotation()}
else{performSegueWithIdentifier("edit", sender: nil)}
}
}
seems not to work as it should. The problem is that removeAnnotation() is called always even when I touch title, not the button. How can I fix that?
Add the info right button in the
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
// add button here, i.e.
let btn = UIButton(type: .Custom)
btn.frame = CGRectMake(0, 0, 30, 30)
btn.setImage(UIImage(named: "info"), forState: .Normal)
annotationView.rightCalloutAccessoryView = btn
// ... other required code
}
Then within the
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
print("Tap")
}
}

How can I create a button with a background color for tvOS while still showing focus?

All I want to do is add a background color to a button for all states. But I want to maintain the automatic focus shadow that you get "for free" when using a system button in the tvOS storyboard. So far I haven't been able to find a combination that allows this.
Alternatively, I would also be interested in a way to programmatically add the shadow when the button is focused, but short of subclassing the button (which I haven't yet tried), I don't know how to do that either.
You can add a shadow for your custom button like this:
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
context.nextFocusedView.layer.shadowOffset = CGSizeMake(0, 10);
context.nextFocusedView.layer.shadowOpacity = 0.6;
context.nextFocusedView.layer.shadowRadius = 15;
context.nextFocusedView.layer.shadowColor = [UIColor blackColor].CGColor;
context.previouslyFocusedView.layer.shadowOpacity = 0;
}
Wasn't happy with a simple colour change, so I made a custom button subclass to look more like the default animation you get with system buttons -
class CustomButton: UIButton
{
private var initialBackgroundColour: UIColor!
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
initialBackgroundColour = backgroundColor
}
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
coordinator.addCoordinatedAnimations(
{
if self.focused
{
self.backgroundColor = UIColor.whiteColor()
UIView.animateWithDuration(0.2, animations:
{
self.transform = CGAffineTransformMakeScale(1.1, 1.1)
},
completion:
{
finished in
UIView.animateWithDuration(0.2, animations:
{
self.transform = CGAffineTransformIdentity
},
completion: nil)
})
}
else
{
self.backgroundColor = self.initialBackgroundColour
}
},
completion: nil)
}
}
Nothing too complicated, but gets the job done
Override didUpdateFocusInContext method and check if next focus view is button, if yes then customize its UI, and to set it back to orignal state check context.previousFocusedView was that button, something like below
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (context.nextFocusedView == _button)
{
// set background color
}
else if (context.previousFocusedView == _button)
{
// set background color to background
}
}
The Ultimate Solution with inspiration from SomaMan. Just subclass all your custom buttons and you Get this:
includes: on tap animation and release and drag away.
//
// CustomFocusButton.swift
//
import UIKit
class CustomFocusButton: UIButton {
let focusedScaleFactor : CGFloat = 1.2
let focusedShadowRadius : CGFloat = 10
let focusedShadowOpacity : Float = 0.25
let shadowColor = UIColor.blackColor().CGColor
let shadowOffSetFocused = CGSizeMake(0, 27)
let animationDuration = 0.2
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
coordinator.addCoordinatedAnimations({
if self.focused{
UIView.animateWithDuration(self.animationDuration, animations:{ [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.clipsToBounds = false
weakSelf.layer.shadowOpacity = weakSelf.focusedShadowOpacity
weakSelf.layer.shadowRadius = weakSelf.focusedShadowRadius
weakSelf.layer.shadowColor = weakSelf.shadowColor
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
},completion:{ [weak self] finished in
guard let weakSelf = self else {return}
if !finished{
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.clipsToBounds = false
weakSelf.layer.shadowOpacity = weakSelf.focusedShadowOpacity
weakSelf.layer.shadowRadius = weakSelf.focusedShadowRadius
weakSelf.layer.shadowColor = weakSelf.shadowColor
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
}
})
} else {
UIView.animateWithDuration(self.animationDuration, animations:{ [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.clipsToBounds = true
weakSelf.transform = CGAffineTransformIdentity
}, completion: {[weak self] finished in
guard let weakSelf = self else {return}
if !finished{
weakSelf.clipsToBounds = true
weakSelf.transform = CGAffineTransformIdentity
}
})
}
}, completion: nil)
}
override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
UIView.animateWithDuration(animationDuration, animations: { [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformIdentity
weakSelf.layer.shadowOffset = CGSizeMake(0, 10);
})
}
override func pressesCancelled(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
if focused{
UIView.animateWithDuration(animationDuration, animations: { [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
})
}
}
override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
if focused{
UIView.animateWithDuration(animationDuration, animations: {[weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
})
}
}
}
I've found something better:
-(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
[coordinator addCoordinatedAnimations:^{
if (self.focused) {
self.backgroundColor = [UIColor whiteColor];
}
else {
self.backgroundColor = [UIColor clearColor];
}
} completion:nil];
}
Swift 4 /tvOS11 and better:
Set the ButtonType in the Interface Builder button properties to "Plain".
Add this private extension to your class:
private extension UIImage {
static func imageWithColor(color: UIColor, size: CGSize) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Then in your connected IBOutlet set the background image for the focused state of the button:
#IBOutlet weak var myButton: UIButton! {
didSet {
let backgroundImageSelected = UIImage.imageWithColor(color: .red, size: myButton.bounds.size)
myButton.setBackgroundImage(backgroundImageSelected, for: .focused)
}
}
You can use the UIButton method setBackgroundImage(image: UIImage?, forState state: UIControlState) and pass through an image that is a flat color and the state .Normal.
This image can easily be created programatically from a UIColor and a size of 1x1:
func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
let rect = CGRectMake(0, 0, size.width, size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
You can set the background image in storyboard to an image that contains the color you would like

Hide buttons from titlebar in Cocoa

The Apples Human Interface Guidelines say:
macOS Human Interface Guidelines: Panels
How do I make the very first titlebar in that image (with only a close button). Disabling both Resize and Minimize in IB only make the resize/minimize buttons get disabled. But I want them to disappear. How can I do that?
I believe this should work:
[[window standardWindowButton:NSWindowCloseButton] setHidden:YES];
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
Swift code for accepted answer
window!.standardWindowButton(.miniaturizeButton)!.isHidden = true
window!.standardWindowButton(.zoomButton)!.isHidden = true
window!.standardWindowButton(.closeButton)!.isHidden = true
I also needed this but visible for mouse overs - Swift:
var trackingTag: NSTrackingRectTag?
override func mouseEntered(with theEvent: NSEvent) {
if trackingTag == theEvent.trackingNumber {
window!.standardWindowButton(.closeButton)!.alphaValue = 1.00
}
}
override func mouseExited(with theEvent: NSEvent) {
if trackingTag == theEvent.trackingNumber {
window!.standardWindowButton(.closeButton)!.alphaValue = 0.01
}
}
func updateTrackingAreas(_ establish : Bool) {
if let tag = trackingTag {
window!.standardWindowButton(.closeButton)!.removeTrackingRect(tag)
}
if establish, let closeButton = window!.standardWindowButton(.closeButton) {
trackingTag = closeButton.addTrackingRect(closeButton.bounds, owner: self, userData: nil, assumeInside: false)
}
}
override func windowDidLoad() {
window!.ignoresMouseEvents = false
updateTrackingAreas(true)
window!.standardWindowButton(.closeButton)!.alphaValue = 0.01
}
func windowShouldClose(_ sender: Any) -> Bool {
window!.ignoresMouseEvents = true
updateTrackingAreas(false)
return true
}
Visibility is needed but just slightly - 0.01 opacity, to have the tracking area effective.
another way is...
for (id subview in [self window].contentView.superview.subviews) {
if ([subview isKindOfClass:NSClassFromString(#"NSTitlebarContainerView")]) {
NSView *titlebarView = [subview subviews][0];
for (id button in titlebarView.subviews) {
if ([button isKindOfClass:[NSButton class]]) {
[button setHidden:YES];
}
}
}
}