how to change UISearchBar from round to rectangle? - iphone

i will like to know how do i need to change a UISearchBar from the default round curve to a rectangle.

for (UIView *searchBarSubview in [mySearchBar subviews]) {
if ([searchBarSubview conformsToProtocol:#protocol(UITextInputTraits)]) {
#try {
[(UITextField *)searchBarSubview setBorderStyle:UITextBorderStyleRoundedRect];
}
#catch (NSException * e) {
// ignore exception
}
}
}
Swift 4:
mySearchBar.subviews().forEach { searchBarSubview in
if searchBarSubview is UITextInputTraits {
do {
(searchBarSubview as? UITextField)?.borderStyle = .roundedRect
} catch {
// ignore exception
}
}
}

Much better in my opinion:
UITextField *txfSearchField = [_searchBar valueForKey:#"_searchField"];
txfSearchField.borderStyle = UITextBorderStyleNone;

Simple add background image, left view and right view for your TextField.
Example below
UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 10., nameField.bounds.size.height)];
nameField.leftView = leftView;
nameField.leftViewMode = UITextFieldViewModeAlways;
leftView release];

UISearchBar has the round curve, and it is fix if you like to make it rectangle you have to use custom search bar with rectangular image as like of search bar and 1 uibutton and UItextfield, and your own searching logic

In the target image I don't see the default shadow, so I'll mention that setting the background property to nil will remove it. I needed to do this in my project and removing the image and manipulating the border works well. I had to cast the control to get to the background property.
UITextField * x = (UITextField*)searchBarSubview;
x.background = nil;
#Keller thanks for the tip for finding the control.

I would use a UIView, with these features:
- set your frame.
- import CoreGraphics in your class
- set a white background of the view
- set view.layer.cornerRadius=10;
- set view.layer.borderWidth=8;
- set view.layer.borderColor=[UIColor blackColor].CGColor;
- insert a label(with a clear background) inside the created view
For the button on the left i would use an uiimage, for the right button set textField.clearButtonMode= UITextFieldViewModeAlways;
Hope this helps.

Why not use the UIAppearance protocol?
[[UITextField appearanceWhenContainedInInstancesOfClasses:#[[UISearchBar class]]] setBorderStyle:UITextBorderStyleRoundedRect];

In our earlier project we also tried to implement in such manner but customization is not possible.

Could you try this.
for (UIView *searchBarSubview in [search_bar subviews]) {
if ([searchBarSubview conformsToProtocol:#protocol(UITextInputTraits)]) {
if([searchBarSubview isKindOfClass:[UITextField class]])
{
[(UITextField *)searchBarSubview setBorderStyle:UITextBorderStyleRoundedRect];
}
}
}

I also recently achieved this in a project by traversing the subviews of the UISearchBar object. However, the text field was not an immediate child as the accepted answer suggested, but a subview of another subview of it. The inner view hierarchy was probably changed at some point. (SDK 8.4)
+ (void)setSearchBar:(UISearchBar *)searchBar cornerRadius:(CGFloat)radius {
//set searchbar corner radius
for(UIView *possibleSearchBarTextFieldSuperview in [searchBar subviews]) {
if([[possibleSearchBarTextFieldSuperview subviews] count] > 0) {
for(UIView *possibleInnerSearchBarTextField in [possibleSearchBarTextFieldSuperview subviews]) {
if([possibleInnerSearchBarTextField conformsToProtocol:#protocol(UITextInputTraits)]) {
possibleInnerSearchBarTextField.layer.cornerRadius = radius;
possibleInnerSearchBarTextField.layer.masksToBounds = YES;
return;
}
}
}
}
}
This method does not use any undocumented methods so it's probably App Store safe, although it is susceptible to stop working with future changes to the SDK.

Swift 3
let textFieldInsideUISearchBar = searchBar.value(forKey: "searchField") as? UITextField
textFieldInsideUISearchBar?.borderStyle = .none
textFieldInsideUISearchBar?.backgroundColor = UIColor.white

The way that it worked with me on Swift 4 is to Subclass the UISearchBar and make the necessary adjustment there without any kind of hacking or workaround ...
I have used the Subclass used in this tutorial and it worked like charm
Here is the code from the example used
//
// SearchBar.swift
// Yomu
//
// Created by Sendy Halim on 9/3/17.
// Copyright © 2017 Sendy Halim. All rights reserved.
//
import Foundation
import UIKit
class SearchBar: UISearchBar {
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
searchBarStyle = .minimal
// Create search icon
let searchIcon = UIImageView(image: #imageLiteral(resourceName: "search"))
let searchImageSize = searchIcon.image!.size
searchIcon.frame = CGRect(x: 0, y: 0, width: searchImageSize.width + 10, height: searchImageSize.height)
searchIcon.contentMode = UIViewContentMode.center
// Configure text field
let textField = value(forKey: "_searchField") as! UITextField
textField.leftView = searchIcon
textField.borderStyle = .none
textField.backgroundColor = UIColor(hex: "#F7F7F7")
textField.clipsToBounds = true
textField.layer.cornerRadius = 6.0
textField.layer.borderWidth = 1.0
textField.layer.borderColor = textField.backgroundColor!.cgColor
textField.textColor = UIColor(hex: "#555555")
}
}

Related

Cannot show Delete button on tableviewcell ios 7

I used UITableviewcellEditingstyleDelete to show button for user click on it to show delete button (it's the same way you can see on email app, when user click edit and then click on button to show the delete button). It's work fine in ios6 but when I build my app on device which have ios 7, the delete button is disappear, but when you tap in the delete button's area it's also can delete. The prolem is user cannot see the delete button (The button which have red color provide by OS).
My code is:
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Detemine if it's in editing mode
if (self.editing)
{
return UITableViewCellEditingStyleDelete;
}
return UITableViewCellEditingStyleNone;
}
please help me to find the solution, I'm not know much with iOS7 enviroment.Thanks!
Thanks all for your advice, and this solution can solved it, I make it into custom cell
UIImageView* backgroundImage;
//Variable for animation delete button
// Keep the ContactView in normal state
BOOL bFirstEditingCell;
BOOL bShownRedMinus;
BOOL bShownDeleteButton;
CGRect frameOfContactViewInNormal;
CGPoint centerOfCellInNormal;
UITableViewCellAccessoryType accessoryTypeInNormal;
//
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super initWithCoder:decoder])
{
super.backgroundColor = [UIColor clearColor];
self.selectionStyle = UITableViewCellSelectionStyleNone;
backgroundImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"row_gradient" ]];
self.backgroundView = backgroundImage;
bShownDeleteButton = false;
bShownRedMinus = false;
bFirstEditingCell = true;
accessoryTypeInNormal = UITableViewCellAccessoryNone;
}
return self;
}
- (void)willTransitionToState:(UITableViewCellStateMask)state {
[super willTransitionToState:state];
if (!isOS7()) {
return;
}
for (UIView *subview in self.subviews) {
//Keep normal value of contact view and cell
if (bFirstEditingCell ) {
frameOfContactViewInNormal = self->contactView.frame;
frameOfContactViewInNormal.size.width = kContactViewWidth;
centerOfCellInNormal = subview.center;
bFirstEditingCell = false;
}
if (state == UITableViewCellStateDefaultMask) {
self.backgroundView = backgroundImage;
subview.center = centerOfCellInNormal;
//Set for position of speed dial image
CGRect f = frameOfContactViewInNormal;
if (bShownRedMinus) {
f.size.width -= kRedMinusButtonWidth;
}
self->contactView.frame = f;
bShownDeleteButton = false;
self.accessoryType = accessoryTypeInNormal;
}
else if (state == UITableViewCellStateShowingDeleteConfirmationMask)
{
float sectionIndexWidth = 0.0;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
sectionIndexWidth = 30.0;
}
else {
sectionIndexWidth = 15.0;
}
CGPoint center = centerOfCellInNormal;
subview.center = CGPointMake(center.x - sectionIndexWidth, center.y);
self.backgroundView = nil;
//Set width of contact name
UIView* view = [subview.subviews objectAtIndex: 0];
CGRect f = view.frame;
f.origin.x = (kDeleteButtonWidth + sectionIndexWidth);
view.frame = f;
f = frameOfContactViewInNormal;
f.size.width = self.frame.size.width - (kDeleteButtonWidth + sectionIndexWidth);
self->contactView.frame = f;
bShownDeleteButton = true;
bShownRedMinus = false;
accessoryTypeInNormal = self.accessoryType;
self.accessoryType = UITableViewCellAccessoryNone;
}
else if (state == UITableViewCellStateShowingEditControlMask) {
CGRect f = frameOfContactViewInNormal;
f.size.width -= 5;
self->contactView.frame = f;
bShownRedMinus = true;
}
else if (state == 3) { //State for clicking red minus button
CGRect f = frameOfContactViewInNormal;
f.size.width += kRedMinusButtonWidth;
self->contactView.frame = f;
self.backgroundView = nil;
}
}
}
It looks like one of the Bug from iOS 7. For some reason the backgroundView is moved by iOS over the delete button. You can work-around this by sub classing your backgroundView and implementing the setFrame function of your derived view like this :
UITableViewCell delete button gets covered up.
It may also happen when the accesoryView is specified and the editingAccessoryView is nil. Detailed explanation of this issue and solution is mentioned here :
UITableViewCell content overlaps delete button when in editing mode in iOS7.
You do not need to check if the tableView is being edited...just implement:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
Best way to remove this problem is that add an image in cell and set it in Backside.
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bgImg.png"]];
imageView.frame = CGRectMake(0, 0, 320, yourCustomCell.frame.size.height);
[yourCustomCell addSubview:imageView];
[yourCustomCell sendSubviewToBack:imageView];
If your text would overlap the delete button then implement Autolayout. It'll manage it in better way.
One more case can be generate that is cellSelectionStyle would highlight with default color. You can set highlight color as follows
yourCustomCell.selectionStyle = UITableViewCellSelectionStyleNone;
Set your table cell's selection style to UITableViewCellSelectionStyleNone. This will remove the blue background highlighting or other. Then, to make the text label or contentview highlighting work the way you want, use this method in yourCustomCell.m class.
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (highlighted)
self.contentView.backgroundColor = [UIColor greenColor];
else
self.contentView.backgroundColor = [UIColor clearColor];
}
I hope it will help you to understand.
Simpler Workaround
Assuming an iOS7 only app, with a technique similar to that linked in the post by Vin above, I believe the approach here: https://stackoverflow.com/a/19416870/535054 is going to be cleaner.
In this approach, you don't need to subclass your backgroundView, which could be different for different cells.
Place the code in the answer I've linked to above, in the root of your custom table cell hierarchy, and all of your table cells (that inherit from it), get the fix whenever they use the backgroundView or selectedBackgroundView properties.
Copy the solution from this gist: https://gist.github.com/idStar/7018104
An easy way to solve this problem is to make the delete confirmation button view as a front view. It can be done by implementing the delegate method layoutSubviews in customtableviewcell.m file.
In my case I solved it by just adding the following code into customtableviewcell.m file. It may be little different according to how the views are placed in your cell. But surely it will give you an idea about how to solve the problem.
- (void)layoutSubviews // called when a interface orientation occur ie. in our case when the '-' button clicks
{
[super layoutSubviews];
for (UIView *subview in self.subviews) { // Loop through all the main views of cell
// Check the view is DeleteConfirmationView or not, if yes bring it into the front
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationView"]) {
[self bringSubviewToFront:subview];// code for bring the view into the front of all subviews
}
}
}

How to add multiple imageView with different shapes?

Currently I am working for a collage app. I want to draw collage frame and after that I need to add images from Camera roll or camera . I need the following type view
I have added 5 UIImageViews. To draw in shape I have added UIBezierPath like for imageview1
UIBezierPath *path1 = [[UIBezierPath alloc] init];
[path1 moveToPoint:CGPointMake(0, 0)];
[path1 addLineToPoint:CGPointMake(150, 0)];
[path1 addLineToPoint:CGPointMake(0, 150)];
[path1 addLineToPoint:CGPointMake(0, 0)];
UIImageView *imgView1 = [[UIImageView alloc] initWithFrame:CGRectMake(140, 0, 160, 300)];
imgView1 . tag = 2;
imgView1 . userInteractionEnabled = YES;
imgView1. backgroundColor = [UIColor greenColor];
CGPathRef borderPathRef2 = [path1 CGPath];
CAShapeLayer *borderShapeLayer2 = [[CAShapeLayer alloc] init];
[borderShapeLayer2 setPath:borderPathRef2];
[[imgView1 layer] setMask:borderShapeLayer2];
imgView1.layer.masksToBounds = YES;
[borderShapeLayer2 release];
[view1 addSubview:imgView1];
Like this I have done for all 5. But after adding imageView5 touch is not detected on all other 4 views because its frame overlaping on the other four imageview.
So I am not getting how to design this . I need to add images to all imageviews by touch action.
Please help me. If someone have any idea about this then please share.
Thanks In Advance.
Following is the example 2 which is from Insta Collage app.
I have solved this problem by overriding hitTest:withEvent: method of UIView.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//Getting View which is touched.
UIView *testView = [super hitTest:point withEvent:event];
//If this is self. and point hitted on Masked Layer(Which is hiding our UIView).
if(testView == self && testView.layer.mask != nil && CGPathContainsPoint([(CAShapedLayer*)testView.layer.mask path], NULL, point, false))
{
//Then return nil. So Touch thinks that UIView has not clicked. and touch is passed to UIView which is behind this view. And again hitTest is performed on inner UIViews.
testView = nil;
}
//Return nil. So touch thinks that UIView is not clicked.
//Return self. So touch thinks self is clicked. and no more hitTest is performed.
return testView;
}
Now i implemented a control called IQIrregularView for this problem.
Check this.
https://github.com/hackiftekhar/IQIrregularView
You should try random shaped buttons
you can find your solution in this custom class found on GitHub
It worked for me and hope will work for you also....
Happy Coding...........
I was also trying to detect a touch on a UIView within the path of a UIBezierPath, using Swift
My scenario is:
- UIView (Container)
- UIImageView (masked with Triangle UIBezierPath)
- UIImageView (masked with Triangle UIBezierPath)
In my container, I override hitTest and return the subview that was touched like so:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in subviews.reversed() {
// ** convert the touch to the subview space:
let convertedPoint = subview.convert(point, from: self)
// ** if the subview layer mask exists I know that it has a UIBezierPath:
if let layerShape = subview.layer.mask as? CAShapeLayer {
if let shapePath = layerShape.path, shapePath.contains(convertedPoint) {
// ** return the subview with the path that contains the converted point
return subview
}
}
}
return nil
}
Hope that helps!

resize UISearchDisplayController dimmed black overlay

anybody know how to resize the dimmed black overly, once you clicked the search bar ?
i having problem when i clicked cancelled the tableview will expend then animated to disappear.
i using this to resize my result tableview.
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
tableView.frame =fTableView.frame;//CGRectMake(26, 100, 280, 310); //fTableView.frame;
tableView.backgroundColor = [UIColor colorWithRed:243.0/255.0 green:236.0/255.0 blue:212.0/255.0 alpha:1];
}
when clicked on the search bar, gray overlay are full instead of my defined size.
when clicked cancel button, the view will expend back.
I combined several answers in order to move the dimmed overlay frame.
1: override UISearchDisplayController class
#interface MySearchController : UISearchDisplayController
2: override setActive function
- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
[super setActive: visible animated: animated];
//move the dimming part down
for (UIView *subview in self.searchContentsController.view.subviews) {
//NSLog(#"%#", NSStringFromClass([subview class]));
if ([subview isKindOfClass:NSClassFromString(#"UISearchDisplayControllerContainerView")])
{
CGRect frame = subview.frame;
frame.origin.y += 10;
subview.frame = frame;
}
}
}
3: change the xib/storyboard Search Display Controller from UISearchDisplayController to
MySearchController
I thought the searchDisplayController owned a seperate tableview, so my guess is that you would need to resize that one.
Something along the lines of: <yourSearchViewController>.view.frame =self.tableView.frame;
or if you don't have it as class variable, in a method which receives it as argument, eg:
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
controller.view.frame = self.tableView.frame;
tableView.backgroundColor = [UIColor colorWithRed:243.0/255.0 green:236.0/255.0 blue:212.0/255.0 alpha:1];
}
Alternativily you might want to subclass it and override its view properties locally.
Hope this helps!
The UISearchDisplayController does owns its own tableview which not as easy to tame.
I came across something like this and am still looking for a better solution.
-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[controller.searchResultsTableView setDelegate:self];
CGFloat gr = 12.0;
controller.searchResultsTableView.backgroundColor = [UIColor colorWithRed:gr green:gr blue:gr alpha:0.0];
[controller.searchResultsTableView setSeparatorStyle:UITableViewCellSelectionStyleNone];
CGRect searchTableFrame = CGRectMake(7, 105, 305, 292);
[controller.searchResultsTableView setFrame:searchTableFrame];
}
The above code does sets the background to transparent but seems to silently ignore the frame size.
EDIT:SOLVED
I found the robust solution to this here.
This saved my life.

Customize UISearchBar: Trying to get rid of the 1px black line underneath the search bar

My question is already specified in the title: I would like to get rid of the black line drawn on the bottom of the UISearchBar. Any ideas?
Here's an image of what I mean:
UPDATE:
I think that the line is part of the UITableView's tableHeaderView. I still don't know how to remove it.
Try this
searchBar.layer.borderWidth = 1;
searchBar.layer.borderColor = [[UIColor lightGrayColor] CGColor];
Why:
So, I've dug into the API's trying to figure out why this is happening. Apparently whomever wrote the UISearchBar API is rasterizing the lines onto an image and setting it as it's backgroundImage.
Solution:
I propose a simpler solution, if you want to set the backgroundColor and get rid of the hairlines:
searchBar.backgroundColor = <#... some color #>
searchBar.backgroundImage = [UIImage new];
Or if you just need a background image without the hairlines:
searchBar.backgroundImage = <#... some image #>
I have 0.5px black horizontal lines both on top and on the bottom of my UISearchBar. The only way I had so far to get rid of them is by setting its style to Minimal:
mySearchBar.searchBarStyle = UISearchBarStyleMinimal;
Solution for
XCode 10.1 Swift 4.2
I fixed this by adding a subview to the searchBar's view stack like so:
CGRect rect = self.searchBar.frame;
UIView *lineView = [[UIView alloc]initWithFrame:CGRectMake(0, rect.size.height-2,rect.size.width, 2)];
lineView.backgroundColor = [UIColor whiteColor];
[self.searchBar addSubview:lineView];
Here, self.searchBar is an UISearchBar pointer of my controller class.
Swift 4.2:
controller.searchBar.layer.borderWidth = 1
controller.searchBar.layer.borderColor = UIColor(red: 255/255, green: 253/255, blue: 247/255, alpha: 1.0).cgColor
Answer based on Ayush's answer.
This is ridiculous to have to go through hoops and bounds for this little 1 px line. I've been googling around for a couple of hours to get rid of it. I tried combos of different answer to get it to work. When I came back here I realized Oxcug already had it but it's in Objective-C and that's not native for me.
Anyway here is the answer in Swift 5. If you want to have a color background inside the actual search textField I added that too.
// these 2 lines get rid of the 1 px line
searchBar.backgroundColor = .white
searchBar.backgroundImage = UIImage()
// this line will let you color the searchBar textField where the user actually types
searchBar.searchTextField.backgroundColor = UIColor.lightGray
Set the tableHeaderView to nil before putting your UISearchBar there.
If that does not help, try to cover it up. First add your search bar to a generic and appropriately sized UIView (say, "wrapper") as a subview, then
CGRect frame = wrapper.frame;
CGRect lineFrame = CGRectMake(0,frame.size.height-1,frame.size.width, 1);
UIView *line = [[UIView alloc] initWithFrame:lineFrame];
line.backgroundColor = [UIColor whiteColor]; // or whatever your background is
[wrapper addSubView:line];
[line release];
And then add it to the tableHeaderView.
self.tableView.tableHeaderView = wrapper;
[wrapper release];
This question has already been solved but maybe my solution can help someone else. I had a similar problem, except I was trying to remove the 1px top border.
If you subclass UISearchBar you can override the frame like this.
- (void)setFrame:(CGRect)frame {
frame.origin.y = -1.0f;
[super setFrame:frame];
self.clipsToBounds = YES;
self.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0, 1.0f);
}
Or if you would like to fix the bottom pixel you could do something like this, (untested).
- (void)setFrame:(CGRect)frame {
frame.origin.y = 1.0f;
[super setFrame:frame];
self.clipsToBounds = YES;
self.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0, -1.0f);
}
Only for simplicity of the example are the clipsToBounds and searchFieldBackgroundPositionAdjustment in the setFrame.
Also the searchFieldBackgroundPositionAdjustment is only needed to re-center the search field.
UPDATE
It turns out that the tableView will shift 1px from updating the origin.y while the searchBar is active. It feels a little strange. I realized that the solution is as simple as setting, self.clipsToBounds = YES;
I used
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.backgroundImage = UIImage() // Removes the line
And it worked like a charm :)
Warning. This is a hack. Would like to know a better, more official way.
You can use the pony debugger to figure out where in the subview hierarchy it is. I think the thing you are see is a private UIImageView called "separator"
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
for (UIView* view in self.searchBar.subviews) {
if (view.frame.size.height == 1 && [view isKindOfClass:[UIImageView class]]) {
view.alpha = 0;
break;
}
}
}
You can use
[[UISearchBar appearance] setSearchFieldBackgroundImage:[UIImage imageNamed:#"someImage.png"]forState:UIControlStateNormal];
on iOS 5+ to get rid of the line.
what worked with me is, setting the searchbar barTintColor as the navbar color
searchBar.barTintColor = UIColor.colorWithHexString(hexStr: "479F46")
after testing, it solves the problem in both iOS 10.x and 11.x
GIF :

Controlling AutoCorrect View Position on iPhone

Is there a way to control the position of the auto correct view that pops up while typing in a UITextField?
By default it appears to always appear below the text field. However in Apple's apps like SMS it sometimes appears above the text field.
For text fields aligned right above the keyboard the auto correct is blocked by the keyboard and not usable.
The position of the auto correction prompt is determined by the firstRect... method of the UITextInput protocol. Unfortunately UITextField uses a private class as delegate for this protocol so you cannot subclass and override it.
You COULD however implement your own UITextField replacement by drawing the contents yourself (like with CoreText), implement your own selection and loupe handling and then you would be able to affect the position of the auto correction prompt. Though it's designed to always be below the edited text, so you would have to essentially lie on the firstRect ... method.
Long story short: it's too much hassle.
One answer it worked for me is to use setInputAccessoryView method of the textview/textview.
I have a toolbar which contains the textView.
If I set the toolbar as the inputaccessoryview of the textfield, and set to NO clipsToBound property of the toolbar, I don't know exactly why, but the balloon appears above the keyboard
Here is a solution using private APIs as there are no ways to do this using public APIs.
Hunt through the application's -windows property to find the private UITextEffectsWindow window and figure out its frame. This is the keyboard
Hunt through the TextView's subviews to find the private UIAutocorrectInlinePrompt view. This is the autocorrect bubble.
Move that subview into a separate wrapper view (added to the TextView) and then move that wrapper view so it's above the above-mentioned keyboard window.
Using swizzled layoutSubViews,
- (void) moveSpellingCorrection {
for (UIView *view in self.subviews)
{
if ([[[view class] description] isEqualToString:#"UIAutocorrectInlinePrompt"])
{
UIView *correctionShadowView = nil; // [view correctionShadowView];
for (UIView *subview in view.subviews)
{
if ([[[subview class] description] isEqualToString:#"UIAutocorrectShadowView"])
{
correctionShadowView = subview;
break;
}
}
if (correctionShadowView)
{
UIView *typedTextView = nil; //[view typedTextView];
UIView *correctionView = nil; //[view correctionView];
for (UIView *subview in view.subviews)
{
if ([[[subview class] description] isEqualToString:#"UIAutocorrectTextView"])
{
if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
{
correctionView = subview;
}
else
{
typedTextView = subview;
}
}
}
if (correctionView && typedTextView)
{
CGRect textRect = [typedTextView frame];
CGRect correctionRect = [correctionView frame];
if (textRect.origin.y < correctionRect.origin.y)
{
CGAffineTransform moveUp = CGAffineTransformMakeTranslation(0,-50.0);
[correctionView setTransform: moveUp];
[correctionShadowView setTransform: moveUp];
CGRect windowPos = [self convertRect: view.frame toView: nil ];
[view removeFromSuperview];
[self.window addSubview: view];
view.frame = windowPos;
}
}
}
}
}
}
For more details check.
In my case, the AutoCorrect view's position is shifted because of the font's leading (line gap).
So, I tried to move it up the leading px by using firstRect function as the code below.
class CustomTextView: UITextView {
override var font: UIFont? {
didSet {
if let font = font {
leadingFont = font.leading
} else {
leadingFont = 0
}
}
}
var leadingFont: CGFloat = 0
override func firstRect(for range: UITextRange) -> CGRect {
var newRect = super.firstRect(for: range)
newRect.origin = CGPoint(x: newRect.origin.x, y: newRect.origin.y - leadingFont)
print("newRect: \(newRect)")
return newRect
}
}
Although it's UITextView but you can do the same thing with UITextField