I have stumbled upon an issue with NSAttributedString and NSTextAttachment in UITextView on iOS16. It works in iOS 15 and appends the images after the text, but in iOS 16 the images are placed on top of the text.
let attributedString = NSMutableAttributedString(string: viewModel.faiModel?.longText ?? "")
attributedString.append(NSAttributedString(string: "\n"))
guard let longTexts = viewModel.faiModel?.longTextModels else { return }
for longText in longTexts.sorted() {
guard let data = longText.binary else { continue }
let image = UIImage(data: data)
// Create an NSTextAttachment and add image
let attachment = NSTextAttachment()
attachment.image = image
// Put your NSTextAttachment into and attributedString and add some spacing
let attString = NSMutableAttributedString(attachment: attachment)
attString.appendSpacing(points: 50)
// Add image after the text
attributedString.append(attString)
}
if let attributedText = makeFirstLineHeadline(attributedString) {
self.textView.attributedText = attributedText
} else {
self.textView.attributedText = attributedString
}
Here's a screenshot of how it looks in iOS 15 (please disregard that it is different pictures):
And how it looks in iOS 16:
I haven't been able to find an official bug report about this for iOS 16. Have any of you experienced the same or have a solution for it?
I have issue that positioning doesn't work anymore (like margin / padding). Same code for iOS 15 is working well.
Related
I am working on Android app that has to put frame and Logo on Image at a Time, The Problem I am facing is Frame is Coming from first Fragment and the Logo from Second Fragment. I am Setting the Frame on Bitmap image on imageView as well as Logo.
The Issue I am facing is, as I successfully Successfully Add Frame on Bitmap image, and I try to also set Logo on Bitmap image it Remove the Frame and Set the Logo on Bitmap and vice versa..
What I really want is Frame and Logo are set on Bitmap at a time...
Here, where Logo Coming From First Fragment Adapter to main Activity via Method..
holder.iconslogo.setOnClickListener {
when (charItemlogo.itemsidlogo) {
1 -> {
var btmp= arrayList[0].iconslogo
(context as MakeStylishActivity).setLogos(btmp)
}
Here the Frame is Coming from Frame Fragment to Main Activity
holder.iconsframe.setOnClickListener {
when (charItemFrame.itemsidframe) {
1 -> {
var btmp= arrayList[0].iconsframe
(context as MakeStylishActivity).setFrames(btmp)
}}
This is Main Activity that is Setting the Logo and Frame to Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_make_stylish)
val byteArray = intent.getByteArrayExtra("pictures")
bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)!!
img_bitmap1.setImageBitmap(bmp)
stringqrcontent= intent.getStringExtra("qrcontent")
bottom_nav_viewstyle.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.action_default -> {
true
}
R.id.action_colors -> {
ShowFColorFragment()
true
}
R.id.action_logos -> {
ShowLogoFragment()
true
}
R.id.action_frames -> {
FunctionAddFrames();
true
}
R.id.action_patterns -> {
true
}
else -> false
}
}
}
fun setLogos(btmp: Bitmap?) {
//img_bitmap1.setImageBitmap(btmp)
PutLogoOnQRBitmaps(btmp, bmp!!)
}
fun setFrames(btmp: Bitmap?) {
// img_bitmap1.setImageBitmap(btmp)
//addWhiteBorder(bmp!!,10)
PutFrameImages(btmp, bmp!!)
}
//Combine Frame Behind QR Code
fun PutFrameImages(frame: Bitmap?, image: Bitmap): Bitmap? {
var cs: Bitmap? = null
var rs: Bitmap? = null
rs = Bitmap.createScaledBitmap(frame!!, image.width, image.height, true)
cs = Bitmap.createBitmap(rs.width, rs.height, Bitmap.Config.RGB_565)
val comboImage = Canvas(cs)
comboImage.drawBitmap(image, 0F, 0F, null)
comboImage.drawBitmap(rs, 0F, 0F, null)
if (rs != null) {
rs.recycle()
rs = null
}
// Runtime.getRuntime().gc()
img_bitmap1.setImageBitmap(cs!!)
return cs
}
//Put Logo on QR Code
fun PutLogoOnQRBitmaps(logo: Bitmap?, qrcode: Bitmap): Bitmap? {
val combined = Bitmap.createBitmap(qrcode.width, qrcode.height, qrcode.config)
val canvas = Canvas(combined)
val canvasWidth = canvas.width
val canvasHeight = canvas.height
canvas.drawBitmap(qrcode, Matrix(), null)
val resizeLogo = Bitmap.createScaledBitmap(logo!!, canvasWidth / 5, canvasHeight / 5, true)
val centreX = (canvasWidth - resizeLogo.width) / 2
val centreY = (canvasHeight - resizeLogo.height) / 2
canvas.drawBitmap(resizeLogo, centreX.toFloat(), centreY.toFloat(), null)
img_bitmap1.setImageBitmap(combined)
return combined
}}
I see a few things here that aren't considered a good idea, but the most important is that the size of an Intent has a limit (very small, that is) and is not designed to pass large amounts of data.
What I would do
Regardless of your simplistic Architecture (no usage of ViewModels, or correct separation of concerns, and a few other SOLID principles ignored here...), I would not pass the image via intent. Instead, I would save the image to the filesystem (temporarily), pass the "path" as a string to the next activity, and have said activity open the file and create the Bitmap from the filesystem.
This means you no longer need to worry about going overboard with the image size/intent size, and that your two activities decouple a little bit. You can now pass any path to a bitmap there and the other activity will pick it up, regardless of where it came from.
A second improvement would be to delegate all this (image storing, passing, retrieving, etc.) to a ViewModel + UseCase (and/or Repository), in which case you'd be further decoupling your code. For this, and much more, the starting point would be getting started with Android Jetpack; I recommend at least trying to leverage a ViewModel in your architecture.
You'd also want to be careful when creating bitmaps out of nowhere, you can easily run out of memory by doing what you're doing; you should take a look at Android's official documentation about handling large bitmaps.
I've been experimenting all day and trying to figure out just how to get my UISearchBar to appear the same in iOS13 as it appears in iOS12/11
So the way the search bar is added is simply a new UISearchController.
var searchController = new UISearchController(searchResultsController: null);
searchController.SearchBar.Placeholder = "Search";
searchController.SearchResultsUpdater = this;
searchController.HidesNavigationBarDuringPresentation = false;
searchController.DimsBackgroundDuringPresentation = false;
NavigationItem.SearchController = searchController;
NavigationItem.HidesSearchBarWhenScrolling = false;
The results on iOS 11/12:
The results on iOS 13:
On iOS 13 I am using the new UINavigationBarAppearance code like this:
var appearance = new UINavigationBarAppearance();
appearance.ConfigureWithOpaqueBackground();
appearance.BackgroundColor = ColorPalette.TintColor;
appearance.TitleTextAttributes = new UIStringAttributes { ForegroundColor = UIColor.White };
NavigationItem.StandardAppearance = appearance;
On iOS 11/12 I am using legacy way to achieve it:
NavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
NavigationController.NavigationBar.TintColor = UIColor.White;
NavigationController.NavigationBar.BarTintColor = ColorPalette.TintColor;
NavigationController.NavigationBar.Translucent = false;
I've tried a number of things, but can't seem to get the UISearchBar to tint by itself how iOS11/12 achieves it.
I know that the new UISearchBar now has access to the UITextField and I can configure the background color's etc.
searchBar.setSearchFieldBackgroundImage(UIImage(), for: .normal)
The code above has a side effect which removes corner radius of the text field.
Extension
extension UISearchBar {
/// This solves iOS 13 issue which is a light gray overay covers the textField.
/// - Parameter color: A color for searchField
func setSearchFieldBackgroundColor(_ color: UIColor) {
searchTextField.backgroundColor = color
setSearchFieldBackgroundImage(UIImage(), for: .normal)
// Make up the default cornerRadius changed by `setSearchFieldBackgroundImage(_:for:)`
searchTextField.layer.cornerRadius = 10
searchTextField.clipsToBounds = true
}
}
There were several properties added in iOS 13, so you need to use them with the help of a conditional. For changing the background color, you have to use the BackgroundColor property of the SearchBar, like this
searchController.SearchBar.BackgroundColor = UIColor.Red;
Use a custom renderer and override the OnElementChanged method this way
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
if(UIDevice.CurrentDevice.CheckSystemVersion(13,0))
Control.SearchTextField.BackgroundColor = Element.BackgroundColor.ToUIColor();
}
}
later on, you don't have to do anything else and worked for me on ios 12 and ios 13+
On iOS 13, you have access to the internal UISearchTextField through the SearchTextField property, you can set it's background directly (in my case, I need the background to be white). Be sure to check the iOS version to avoid exceptions.
if(UIDevice.CurrentDevice.CheckSystemVersion(13,0))
{
searchController.SearchBar.SearchTextField.BackgroundColor = UIColor.White;
}
You can achieve desired result by adding couple of lines.
searchBar.barTintColor = UIColor.red
searchBar.searchTextField.backgroundColor = UIColor.white
Before you try this code please link IB Outlets for searchBar
#IBOutlet weak var searchBar: UISearchBar!
This code sample was working in a macOS playground:
import Cocoa
import XCPlayground
func getResImg(name: String, ext: String) -> CIImage {
guard let fileURL = Bundle.main.url(forResource: name, withExtension: ext) else {
fatalError("can't find image")
}
guard let img = CIImage(contentsOf: fileURL) else {
fatalError("can't load image")
}
return img
}
var img = getResImg(name: "noise", ext: "jpg")
After upgrading to Swift 4.1 it doesn't. Error: Failed to get a bitmap representation of this NSImage.
How does it work now in Swift 4.1?
I ran into the same issue even though I was using a macOS project and wasn't able to pinpoint what's going wrong here, but I found a workaround which fixes playground rendering for CIImage by using a CustomPlaygroundDisplayConvertible
Just add the following code to the top of your playground and your images will render again
extension CIImage: CustomPlaygroundDisplayConvertible {
static let playgroundRenderContext = CIContext()
public var playgroundDescription: Any {
let jpgData = CIImage.playgroundRenderContext.jpegRepresentation(of: self, colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, options: [:])!
return NSImage(data: jpgData)!
}
}
I've just upgraded my project from cocos2d 1.0.1 to 2.0 and after a lot of tweaking and all, I'm unable to change the default color of a CCLabelTTF like I did before (And this way I avoid one line of code for each label I create). Before, I was doing like that :
In CCLabelTTF.m :
- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size
{
if( (self=[super init]) ) {
dimensions_ = CGSizeMake( dimensions.width * CC_CONTENT_SCALE_FACTOR(), dimensions.height * CC_CONTENT_SCALE_FACTOR() );
alignment_ = alignment;
fontName_ = [name retain];
fontSize_ = size * CC_CONTENT_SCALE_FACTOR();
lineBreakMode_ = lineBreakMode;
color_ = ccBLACK;
[self setString:str];
}
return self;
}
I was changing the color inside this method since every "initWithString..." methods are returning this one, but even if I do so in cocos2D 2.0, it doesn't work.
Here's my new CCLabelTTF.m :
- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size
{
if( (self=[super init]) ) {
// shader program
self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:SHADER_PROGRAM];
dimensions_ = dimensions;
hAlignment_ = alignment;
vAlignment_ = vertAlignment;
fontName_ = [name retain];
fontSize_ = size;
lineBreakMode_ = lineBreakMode;
color_ = ccBLACK;
[self setString:str];
}
return self;
}
Is it because of the "ShaderProgram" thingy that wasn't there before 2.0? Please help I've tried everything so far :(
I even searched in all my project if there was a file containing "ccWHITE" or "{255,255,255}" but there's none related to CCLabelTTF (except for CCSprite, but if I change it to ccBLACK, all my sprites becomes black)
Instead of setting the ivar, use the accessor for the property:
self.color = ccBlack;
Also, you should not modify CCLabelTTF. If you want to change behaviour, make a subclass.
Im trying to get the OCR sample app to recognise some small text and how I'm doing it is to resize the image. Once I have resized the image it is all 'pixel-ee'
I want to use the SmothGaussian method to clean it up but I get an error each time I execute the method
Here is the code:
Image<Bgr, Byte> image = new Image<Bgr, byte>(openImageFileDialog.FileName);
using (Image<Gray, byte> gray = image.Convert<Gray, Byte>().Resize(800, 600, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR, true))
{
gray.Convert<Gray, Byte>()._SmoothGaussian(4);
_ocr.Recognize(gray);
Tesseract.Charactor[] charactors = _ocr.GetCharactors();
foreach (Tesseract.Charactor c in charactors)
{
image.Draw(c.Region, drawColor, 1);
}
imageBox1.Image = image;
//String text = String.Concat( Array.ConvertAll(charactors, delegate(Tesseract.Charactor t) { return t.Text; }) );
String text = _ocr.GetText();
ocrTextBox.Text = text;
}
Here is the image:
_SmoothGaussian can only handle odd numbers as kernel size so try with 3 or 5 as argument instead.