Since iOS 11 and Xcode 9 barbuttonitems and titles aren't visible anymore. It doesn't matter whether I'm trying to add a custom view like this:
let backButton = UIButton.init(frame: CGRect(x: 0, y: 0, width: 180, height: 32))
backButton.setImage(UIImage(named: "back_icon")?.withRenderingMode(.alwaysTemplate), for: .normal)
backButton.tintColor = UIColor.white
backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
backButton.backgroundColor = UIColor.clear
backButton.titleLabel?.font = UIFont(name: SWMainHelper.sharedInstance.mediumFont, size: 18)
backButton.setTitleColor(UIColor.white, for: .normal)
backButton.setTitle("Go back", for: .normal)
backButton.sizeToFit()
backButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0)
backButton.frame.size.width += 16
let negativeButtonSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
negativeButtonSpace.width = -16
self.navigationItem.setLeftBarButtonItems([negativeButtonSpace, UIBarButtonItem(customView: backButton)], animated: true)
or just standard UIBarButtonItems like that:
let add = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTapped))
add.tintColor = UIColor.white
let play = UIBarButtonItem(title: "Play", style: .plain, target: self, action: #selector(playTapped))
play.tintColor = UIColor.white
navigationItem.rightBarButtonItems = [add, play]
With Xcode 8 everything worked fine.
This issue comes while compiling with Xcode 9,because now UIBarButtonItem is using autolayput as well.Below is the code it to make it work.
UIButton *leftCustomButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 35, 100)];
[leftCustomButton.widthAnchor constraintEqualToConstant:100].active = YES;
[leftCustomButton.heightAnchor constraintEqualToConstant:35].active = YES;
[leftCustomButton setTitle:#"TEST" forState:UIControlStateNormal];
[leftCustomButton.titleLabel setFont:[UIFont boldSystemFontOfSize:16.0]];
[leftCustomButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
UIBarButtonItem * leftButtonItem =[[UIBarButtonItem alloc] initWithCustomView:leftCustomButton];
[self.navigationItem setRightBarButtonItems:#[leftButtonItem]];
I had the same issue when updating to the xCode9 IDE. I was able to resolve this by using UINavigationBar.appearance:
let buttonItem = UIButton.appearance(whenContainedInInstancesOf: [UINavigationBar.self])
buttonItem.setTitleColor(.black, for: .normal)
buttonItem.setTitleColor(.gray, for: .disabled)
I had that same issue and none of the above fixed.
All I did was to set a width on the titleView and everything worked just fine!
EDIT:
Every UIViewController has a navigationItem property, and every navigationItem has an optional titleView.
For reference: https://developer.apple.com/documentation/uikit/uinavigationitem/1624935-titleview
In my case, I was using a custom titleView and I think that's the cause of the problem, since Apple changed the API to support the new navigation bar layout.
Related
In iOS 13, I'm getting a crash when accessing the UITextField _placeholderLabel.textColor label key.
The key used to apply placeholder text color.
[textfield setValue:[UIColor whiteColor] forKeyPath:#"_placeholderLabel.textColor"];
"NSGenericException" - reason: "Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug"
You can do it by using runtime:
add the following code to the bottom of placeholder setting
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
placeholderLabel.textColor = [UIColor whiteColor];
At Xcode 11 beta2 ,this code is work ,but I don't know about GM version or official version.
The complete code:
Objective-C Version
#import "ViewController.h"
#import <objc/runtime.h>
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
self.title = #"UITextField Demo";
UITextField *textField = [UITextField new];
textField.frame = CGRectMake(0, 100, 300, 50);
textField.placeholder = #"UITextField Demo";
[self.view addSubview:textField];
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
placeholderLabel.textColor = [UIColor whiteColor];
}
#end
Swift Version:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
let textField = UITextField()
textField.frame = CGRect(x: 0, y: 100, width: 300, height: 50)
textField.placeholder = "UITextField Demo"
view.addSubview(textField)
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(textField, iVar) as! UILabel
placeholderLabel.textColor = .red
}
}
2019/09/25 Update
The above implementation can solve the problem ,but it not be advocated.
The apps that use the private api maybe broken in the future.
Please use new api :
var attributedPlaceholder: NSAttributedString? { get set }
Discussion
This property is nil by default. If set, the placeholder string is drawn using system-defined color and the remaining style information (except the text color) of the attributed string. Assigning a new value to this property also replaces the value of the placeholder property with the same string data, albeit without any formatting information. Assigning a new value to this property does not affect any other style-related properties of the text field.
The complete code:
let textField = UITextField()
textField.frame = CGRect(x: 0, y: 100, width: 300, height: 50)
let placeholderString = NSAttributedString.init(string: "UITextField Demo", attributes: [NSAttributedString.Key.foregroundColor : UIColor.red])
textField.attributedPlaceholder = placeholderString
view.addSubview(textField)
I think it's a bug from the XCode side and hopefully, they will fix this one on the next release. You can quickly fix this by Erase all Contents and Settings on the Simulator device.
Yes this problem is related with the Xcode version. I have Xcode 11.2.1 and i am facing same issue.
_placeholderLabel.textColor
if you are using it from storyboard as runtime var and getting issue so just remove "_"
like that
after removing the "_" runtime Var start working
Remove Underscore "_" and try this.
[textfield setValue:[UIColor whiteColor] forKeyPath:#"placeholderLabel.textColor"];
It should work for all versions!
I did it this way :
Objc
NSMutableAttributedString *placeholderAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:searchField.attributedPlaceholder];
[placeholderAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [placeholderAttributedString length])];
searchField.attributedPlaceholder = placeholderAttributedString;
Swift 5
var placeholderAttributedString = NSMutableAttributedString(attributedString: searchField.attributedPlaceholder)
placeholderAttributedString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: placeholderAttributedString.length))
searchField.attributedPlaceholder = placeholderAttributedString
It's a bit long but I have nothing better for the moment.
if you are using UITextView+Placeholder.h class please change below method Clean and build if issue persists
Objective C
+(UIColor *)defaultPlaceholderColor {
static UIColor *color = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UITextField *textField = [[UITextField alloc] init];
textField.placeholder = #" ";
// color = [textField valueForKeyPath:#"_placeholderLabel.textColor"];
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(textField, ivar);
color = placeholderLabel.textColor;
});
return color;
}
You can set UITextField placeholder text color, font using attributedPlaceholder method as follows :
NSString *text = [textField text];
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:text attributes:#{NSForegroundColorAttributeName: [UIColor redColor], NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue-Medium" size:14.0f]}];
"Erase all contents and settings"
This worked for me.
I removed the var "_placeholderLabel" in the section "User Defined Runtime Attributes"
Just replace _placeholderLabel with placeholderLabel
For IOS 13 you can set UITextField placeholder color by one line code.
Objective C
[textField setAttributedPlaceholder:[[NSAttributedString alloc] initWithString:#"PlaceHolder Text" attributes:#{NSForegroundColorAttributeName:[UIColor whiteColor]}]];
In Swift 5
txtTitle.attributedPlaceholder = NSAttributedString(string:"PlaceHolder Text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])
- Swift Version
Add the following code to the bottom of placeholder setting:
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(textField, iVar) as! UILabel
placeholderLabel.textColor = .white
For example:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
let textField = UITextField()
textField.frame = CGRect(x: 0, y: 100, width: 300, height: 50)
textField.placeholder = "UITextField Demo"
view.addSubview(textField)
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(textField, iVar) as! UILabel
placeholderLabel.textColor = .white
}
Swift 5
Here is a correct Swift replacement of that property.
Note that if you also change alignment of other properties for the text field, then you need to set these properties to placeholder's attributed text too. Usually only color and font are changed:
/// Set placeholder text color
/// - Parameter color: the color
func setPlaceholderColor(_ color: UIColor) {
// Color
var attributes: [NSAttributedString.Key: Any] = [.foregroundColor: color]
var range = NSRange(location: 0, length: 1)
// Font
if let text = attributedText, text.length > 0, let attrs = attributedText?.attributes(at: 0, effectiveRange: &range), let font = attrs[.font] {
attributes[.font] = font
}
else if let font = font {
attributes[.font] = font
}
self.attributedPlaceholder = NSAttributedString(string: self.placeholder ?? "", attributes: attributes)
}
FOR UISEARCHBAR PLACEHOLDER COLOR
Xcode 11.1
From iOS 13 onwards the SDK provides UISearchBar.searchTextField so we can use
public API instead of private API. In the subclass of UISearchBar i have used this code to change the placeholder color
UITextField *textField = [self findSubviewOfClass:[UITextField class]];
textField.textColor = [UIColor whiteColor];
//textField.font = [UIFont fontWithName:#"HelveticaNeue-Regular" size:14.0f];
if (#available(iOS 13.0, *)) {
self.searchTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:#{NSForegroundColorAttributeName: [UIColor colorWithRed:0.8 green:0.82 blue:0.81 alpha:1]}];
}else {
[textField setValue:[UIColor colorWithRed:0.8 green:0.82 blue:0.81 alpha:1] forKeyPath:#"_placeholderLabel.textColor"];
}
Remove "_" form "_placeholderLabel.textColor" try with "placeholderLabel.textColor".
I have this code that shares a screenshot of the app, an initial Text and a URL via the Apple Share menu when a Share Button is pressed. It works like a charm with Twitter, Messages, Email etc, but when i wanna share to Facebook or the Facebook Messenger, the text doesn't show up (but the image and the URL do).
Any idea why that is and how to fix it?
I was able to share text and a URL to Facebook with other functions (but they didn't deliver the right screenshot so i switched to this one).
Thanks in advance!
Edit: The text does get shared in the xcode Simulator, just not on the iPhone(s).
func takeSnapshot(view: UIView) {
let bounds = UIScreen.mainScreen().bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, true, 0.0)
self.view.drawViewHierarchyInRect(bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let text = "Text to be shared... #Hashtag"
let URL = NSURL(string: "http://stackoverflow.com/")!
let activityViewController = UIActivityViewController(activityItems: [image, text, URL], applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: nil)
}
An update to the Facebook application has replaced the in-built Facebook share activity with one that ignores status text - If you remove the Facebook app from your device the text will be displayed using the code you have. If you have the Facebook app installed you get images, URLs but not the text.
Follow the instructions: https://developers.facebook.com/docs/sharing/ios
Then you can do something like this:
Share a link
let content : FBSDKShareLinkContent = FBSDKShareLinkContent()
content.contentURL = NSURL(string: "<INSERT STRING HERE>")
content.contentTitle = "<INSERT STRING HERE>"
content.contentDescription = "<INSERT STRING HERE>"
content.imageURL = NSURL(string: "<INSERT STRING HERE>")
let button : FBSDKShareButton = FBSDKShareButton()
button.shareContent = content
button.frame = CGRectMake((UIScreen.mainScreen().bounds.width - 100) * 0.5, 50, 100, 25)
self.view.addSubview(button)
Share Photos
class ViewController: UIViewController, FBSDKLoginButtonDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate
let button : UIButton = UIButton()
button.backgroundColor = UIColor.blueColor()
button.setTitle("Choose Photo", forState: .Normal)
button.frame = CGRectMake((UIScreen.mainScreen().bounds.width - 150) * 0.5, 125, 150, 25)
button.addTarget(self, action: "photoBtnClicked", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
let label : UILabel = UILabel()
label.frame = CGRectMake((UIScreen.mainScreen().bounds.width - 200) * 0.5, 100, 200, 25)
label.text = "Photos Example"
label.textAlignment = .Center
self.view.addSubview(label)
func photoBtnClicked(){
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.SavedPhotosAlbum){
println("Photo capture")
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.SavedPhotosAlbum;
imagePicker.allowsEditing = false
self.presentViewController(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let photo : FBSDKSharePhoto = FBSDKSharePhoto()
photo.image = info[UIImagePickerControllerOriginalImage] as! UIImage
photo.userGenerated = true
let content : FBSDKSharePhotoContent = FBSDKSharePhotoContent()
content.photos = [photo]
}
Photos must be less than 12 MB in size
Share Video on Facebook
class ViewController: UIViewController, FBSDKLoginButtonDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate
let button : UIButton = UIButton()
button.backgroundColor = UIColor.blueColor()
button.setTitle("Choose Video", forState: .Normal)
button.frame = CGRectMake((UIScreen.mainScreen().bounds.width - 150) * 0.5, 200, 150, 25)
button.addTarget(self, action: "videoBtnClicked", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
let label : UILabel = UILabel()
label.frame = CGRectMake((UIScreen.mainScreen().bounds.width - 200) * 0.5, 175, 200, 25)
label.text = "Video Example"
label.textAlignment = .Center
self.view.addSubview(label)
func videoBtnClicked(){
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSource Type.SavedPhotosAlbum){
println("Video capture")
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.SavedPhotosAlbum
imagePicker.allowsEditing = false
self.presentViewController(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let video : FBSDKShareVideo = FBSDKShareVideo()
video.videoURL = info[UIImagePickerControllerMediaURL] as! NSURL
let content : FBSDKShareVideoContent = FBSDKShareVideoContent()
content.video = video
}
Videos must be less than 12MB in size. I hope I have helped you. (Sorry for my english)
I have an app which is not using auto layout. Instead I'm using autoresizingMask. It works well in iOS 7. And it also works fine in iOS 8 when I start the app in portrait mode (it's still fine even when I rotate the device afterwards). Here is the screenshot when it's correct:
Here is my source code:
CGFloat w = [[UIScreen mainScreen]bounds].size.width;
CGFloat h = 100;
CGRect rect1 = CGRectMake(0, 0, w, h);
UIView* view1 = [[UIView alloc] initWithFrame:rect1];
view1.backgroundColor = [UIColor yellowColor];
view1.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:view1];
CGRect rect2 = CGRectMake(10, 105, 748, 50);
UIView* view2 = [[UIView alloc]initWithFrame:rect2];
view2.backgroundColor = [UIColor purpleColor];
view2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.view addSubview:view2];
The problem occurs when I start my app in landscape mode. Here is the screenshot:
Can anybody tell what I did wrong in my code? Thanks
I modified the code a bit:
CGRect rect2 = CGRectMake((w-748)/2, 105, 748, 50);
This works as I expected.
-(void)drawRect : (NSRect)rect
{
imgRect.orgin = NSZeroPoint;
imgRect.size = [appleImage size];
drawRect = [self bounds];
[appleRect drawInRect:appleRect
fromRect:imgRect
operation:NSCompositeSourceOver
fraction:1.0];
...
}
I'm trying this but it's not easy and driving me crazy.
How to change NSRect type code to CGRect type code ?
plz help!
ps. i already know.. NSRect -> CGRect , NSZeroPoint -> CGRectZero
Just use these 2 Cocoa (no iOS) functions:
CGRect NSRectToCGRect(NSRect nsrect);
NSRect NSRectFromCGRect(CGRect cgrect);
Have a look here to deepen the topic: http://cocoadev.com/CGRect
If you encounter this in iOS, such as with UIKeyboardFrameEndUserInfoKey, it's important to know that NSRect is a subclass of NSValue.
if let rectValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let rect = rectValue.CGRectValue()
// Use your CGRect
}
Assuming you're using Apple's Foundation and not GNUStep or something else odd...
Swift
In Swift, NSRect is just a typealias of CGRect (NSGeometry.swift line 449-ish), so you can simply use either in place of the other anywhere.
let nsTest1 = NSRect(x: 1, y: 2, width: 3, height: 4)
let cgTest1 = CGRect(x: 1, y: 2, width: 3, height: 4)
let cgTest2 = CGRect(x: 100, y: 200, width: 300, height: 400)
print(nsTest1 == cgTest1) // true
func takeCgRect(_ rect: CGRect) {
print("A CoreGraphics rectangle at \(rect.origin) with size \(rect.size)")
}
func takeNsRect(_ rect: NSRect) {
print("A NeXTStep rectangle at \(rect.origin) with size \(rect.size)")
}
takeCgRect(nsTest1) // A core graphics rectangle at (1.0, 2.0) with size (3.0, 4.0)
takeNsRect(cgTest2) // A NeXTStep rectangle at (100.0, 200.0) with size (300.0, 400.0)
Objective-C
In Objective-C, NSRect is just a typedef of CGRect (NSGeometry.h line 26-ish), so you can simply use either in place of the other anywhere.
void takeCgRect(CGRect rect) {
NSLog(#"A CoreGraphics rectangle at %# with size %#", NSStringFromCGPoint(rect.origin), NSStringFromCGSize(rect.size))
}
void takeNsRect(NSRect rect) {
NSLog(#"A NeXTStep rectangle at %# with size %#", NSStringFromPoint(rect.origin), NSStringFromSize(rect.size))
}
// ELSEWHERE:
NSRect nsTest1 = NSMakeRect(1, 2, 3, 4)
CGRect cgTest1 = CGRectMake(1, 2, 3, 4)
CGRect cgTest2 = CGRectMake(100, 200, 300, 400)
NSLog(#"%#", #(nsTest1 == cgTest1)) // 1
takeCgRect(nsTest1) // A core graphics rectangle at {1.0, 2.0} with size {3.0, 4.0}
takeNsRect(cgTest2) // A NeXTStep rectangle at {100.0, 200.0} with size {300.0, 400.0}
SWIFT 5
It works for me. Use 'if' before let.
let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
print("\(String(describing: keyboardFrame?.height))")
I add custom button to navigation controller
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"back.png"]
style:UIBarButtonItemStylePlain
target:self
action:#selector(backAction)];
self.navigationItem.leftBarButtonItem = backButton;
it works fine, but button appears bordered. How can I fix that?
UPDATE
I found solution
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[button setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonFavoriteClicked) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithCustomView:button];
[button release];
self.navigationItem.leftBarButtonItem = back;
Try this.
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, button_width, button_height)];
[backButton setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backAction) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
For Swift 4
let backButton = UIButton(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
backButton.setImage(UIImage(named: "back.png"), for: .normal)
backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: backButton)
And action (selector) should be like the following :
#objc func backAction () {
// do the magic
}
Adding Button either in left or right with title or without title as per the requirement,
func addBarButton(image: UIImage?, isLeft: Bool = true, title: String? = nil, font: UIFont = UIFont.systemFont(ofSize: 16), tintColor: UIColor = R.color.textfieldColor() ?? .black) {
if let navController = self.navigationController {
let navBar = navController.navigationBar
navBar.isHidden = false
navBar.isTranslucent = false
let btn = UIButton.init(type: .custom)
if let img = image {
btn.setImage(img, for: UIControl.State.normal)
}
if let titleTxt = title {
btn.setTitle(titleTxt, for: .normal)
btn.setTitleColor(tintColor, for: .normal)
btn.titleLabel?.font = font
}
btn.frame = CGRect.init(x: 0, y: 0, width: 35, height: 35)
btn.adjustsImageWhenHighlighted = false
let barButton = UIBarButtonItem.init(customView: btn)
if isLeft {
btn.addTarget(self, action: #selector(self.btnLeftNavigationClicked), for: UIControl.Event.touchUpInside)
btn.tintColor = tintColor
if let tabBar = self.tabBarController {
tabBar.navigationItem.leftBarButtonItem = nil
tabBar.navigationItem.leftBarButtonItem = barButton
} else {
self.navigationItem.leftBarButtonItem = nil
self.navigationItem.leftBarButtonItem = barButton
}
} else {
btn.addTarget(self, action: #selector(self.btnRightNavigationClicked), for: UIControl.Event.touchUpInside)
btn.tintColor = tintColor
if let tabBar = self.tabBarController {
tabBar.navigationItem.rightBarButtonItem = barButton
} else {
self.navigationItem.rightBarButtonItem = barButton
}
}
}
}
Right button action
#objc func btnRightNavigationClicked(sender: UIButton) {
// add your logic here
}
Left button action
#objc func btnLeftNavigationClicked(sender: UIButton) {
guard let navigationController = self.navigationController else {
return
}
navigationController.popViewController(animated: true)
}