কোনও ইউআইভিউকে পুনরায় চিত্রায়িত করতে বাধ্য করার সবচেয়ে মজাদার উপায় কী?


117

আইটেমের তালিকা সহ আমার একটি ইউআইটিএবলভিউ রয়েছে। কোনও আইটেম নির্বাচন করা এমন একটি ভিউকন্ট্রোলারকে ধাক্কা দেয় যা তারপরে নিম্নলিখিতটি করতে এগিয়ে যায়। পদ্ধতি ভিউডিডিএলএড থেকে আমি এমন একটি ইউআরএল অনুরোধ করব যাতে আমার সাবউভিউগুলি দ্বারা প্রয়োজনীয় ডেটার জন্য অনুরোধ করা যায় - ড্রইরেক্টের ওভাররাইডযুক্ত একটি ইউআইভিউ সাবক্লাস। ক্লাউড থেকে ডেটা এলে আমি আমার ভিউ হায়ারার্কি তৈরি করা শুরু করি। প্রশ্নে সাবক্লাসটি ডেটা পাস হয়ে যায় এবং এটির ড্র্যাক্ট পদ্ধতিতে এখন রেন্ডার করার জন্য প্রয়োজনীয় সমস্ত কিছু রয়েছে।

কিন্তু।

যেহেতু আমি স্পষ্টরূপে ড্রআরেক্টকে কল করি না - কোকো-টাচ হ্যান্ডেল করে যে - আমি সত্যিই এই ইউআইভিউ সাবক্লাসটি রেন্ডার করতে চাই যে কোকো-টাচকে জানানোর আমার কোনও উপায় নেই। কখন? এখন ভাল হবে!

আমি [আমারভিউ সেটনিডসডিসপ্লে] চেষ্টা করেছি। এই কিন্ডা মাঝে মাঝে কাজ করে। খুব দাগযুক্ত।

আমি ঘন্টা এবং ঘন্টা এই সাথে কুস্তি করছি। যিনি দয়া করে আমাকে ইউআইভিউ পুনরায় রেন্ডার করতে বাধ্য করার জন্য একটি শিলা শক্ত, গ্যারান্টিযুক্ত পদ্ধতির সাথে দয়া করে সরবরাহ করতে পারেন।

কোডটির স্নিপেট এখানে দেওয়া হয়েছে যা দেখার জন্য ডেটা ফিড করে:

// Create the subview
self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease];

// Set some properties
self.chromosomeBlockView.sequenceString     = self.sequenceString;
self.chromosomeBlockView.nucleotideBases    = self.nucleotideLettersDictionary;

// Insert the view in the view hierarchy
[self.containerView          addSubview:self.chromosomeBlockView];
[self.containerView bringSubviewToFront:self.chromosomeBlockView];

// A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-)
[self.chromosomeBlockView setNeedsDisplay];

চিয়ার্স, ড

উত্তর:


193

UIViewপুনরায় রেন্ডার করতে বাধ্য করার জন্য গ্যারান্টিযুক্ত, রক শক্ত উপায় [myView setNeedsDisplay]। যদি আপনার এটি নিয়ে সমস্যা হয় তবে আপনি সম্ভবত এই সমস্যাগুলির মধ্যে একটিতে চলেছেন:

  • আপনার কাছে আসলে ডেটা থাকার আগেই আপনি এটি কল করছেন বা আপনার কোনও -drawRect:কিছু অতিরিক্ত-ক্যাশে করছে।

  • আপনি এই পদ্ধতিটি কল করার মুহুর্তে আপনি দৃশ্যটি আঁকানোর প্রত্যাশা করছেন। কোকো অঙ্কন সিস্টেমটি ব্যবহার করে "ঠিক এখনই এই দ্বিতীয় দ্বিতীয়টি" আঁকানোর দাবি করার কোনও উপায় নেই। এটি পুরো ভিউ সংমিশ্রণ সিস্টেম, আবর্জনার কার্যকারিতা ব্যাহত করবে এবং সম্ভবত সব ধরণের শিল্পকর্ম তৈরি করবে। কেবলমাত্র বলার উপায় আছে "এটি পরবর্তী ড্র চক্রে আঁকতে হবে"।

আপনার যা দরকার তা যদি "কিছু যুক্তি, আঁকুন, আরও কিছু যুক্তি" হয় তবে আপনাকে "আলাদা কিছু পদ্ধতিতে" আরও কিছু যুক্তি যুক্ত করতে হবে -performSelector:withObject:afterDelay:এবং ০. দেরি করে এটি প্রার্থনা করতে হবে যা "আরও কিছু যুক্তি" পরে রাখবে পরবর্তী অঙ্কন চক্র এই ধরণের কোডের উদাহরণের জন্য এই প্রশ্নটি দেখুন এবং এটির প্রয়োজন হতে পারে এমন একটি ক্ষেত্রে দেখুন (যদিও কোডটি জটিল করে তোলে তবে সম্ভব হলে অন্যান্য সমাধানগুলি সন্ধান করা সাধারণত সেরা)।

আপনি যদি মনে করেন না যে জিনিসগুলি টানা হচ্ছে, একটি ব্রেকপয়েন্ট রেখে দিন -drawRect:এবং কখন আপনাকে ডাকা হবে। যদি আপনি কল করছেন -setNeedsDisplay, কিন্তু -drawRect:পরবর্তী ইভেন্টের লুপে কল না পেয়ে থাকেন, তবে আপনার দর্শন শ্রেণিবিন্যাসটি খনন করুন এবং নিশ্চিত করুন যে আপনি আউটমার্ট চেষ্টা করছেন না কোথাও। অত্যধিক চতুরতা আমার অভিজ্ঞতাতে খারাপ অঙ্কনের # 1 কারণ। যখন আপনি মনে করেন যে সিস্টেমটি কীভাবে চান তা করার কৌশলটি আপনি কীভাবে ভালভাবে জানেন তবে আপনি সাধারণত যা চান না ঠিক তা করে যান।


রব, আমার চেকলিস্ট এখানে। 1) আমার কাছে ডেটা আছে? হ্যাঁ. আমি একটি পদ্ধতিতে ভিউটি তৈরি করি - আড়ালস্পিনার - সংযোগটি থেকে মূল থ্রেডে ডিডফিনিশলোডিং: এইভাবে: [স্ব-সম্পাদনাগ্রাহকঅনমেনথ্রেড: @ সিলেক্টর (হাইডস্পিনার) অবজেক্ট সহ: নিল ওয়েটউন্টিলডোন: না]; 2) আমার তাত্ক্ষণিকভাবে অঙ্কনের দরকার নেই। আমার শুধু এটি দরকার আজ. বর্তমানে এটি সম্পূর্ণ এলোমেলো এবং আমার নিয়ন্ত্রণের বাইরে। [আমারভিউ সেটনিডসডিসপ্লে] সম্পূর্ণ অবিশ্বাস্য। এমনকি ভিডিডিঅ্যাপারে [মাইভিউ সেটনিডসডিসপ্লে] কল করতে আমি এতদূর চলে গিয়েছি :. Nuthin '। কোকো আমাকে কেবল উপেক্ষা করে। উন্মাদক !!
ডুগলা

1
আমি খুঁজে পেয়েছি যে এই পদ্ধতির সাথে প্রায়শই setNeedsDisplayআসল কল এবং এর মধ্যে বিলম্ব হয় drawRect:। কলটির নির্ভরযোগ্যতাটি এখানে থাকাকালীন আমি এটিকে "সবচেয়ে দৃ .়" সমাধানটি বলব না - তাত্ত্বিকভাবে, ক্লায়েন্ট কোডটিতে ড্র করার দাবিতে ফিরে আসার আগে তাত্ক্ষণিকভাবে সমস্ত অঙ্কন করা উচিত।
স্লিপ ডি থম্পসন

1
আমি আরও উত্তর সহ আপনার উত্তর নিচে মন্তব্য। ডকুমেন্টেশনগুলিকে কল করে পদ্ধতিগুলিকে ডেকে আনার জন্য অঙ্কন সিস্টেমকে চাপের বাইরে চলে যেতে বাধ্য করার চেষ্টা করা স্পষ্টভাবে বলছে কল না করা শক্ত নয়। সর্বোপরি এটি অপরিবর্তিত আচরণ। সম্ভবত এটি কর্মক্ষমতা এবং অঙ্কনের গুণমানকে হ্রাস করবে। সবচেয়ে খারাপ সময়ে এটি অচল বা ক্রাশ হতে পারে।
রব নেপিয়ার

1
যদি আপনাকে ফেরার আগে আঁকার দরকার হয় তবে একটি চিত্রের প্রসঙ্গে আঁকুন (যা ক্যানভাসের মতো অনেকের প্রত্যাশা মতো কাজ করে)। কম্পোজিটিং সিস্টেমটি চালানোর চেষ্টা করবেন না। এটি সর্বনিম্ন সংস্থান ওভারহেড সহ উচ্চ মানের গ্রাফিক্স সরবরাহ করার জন্য ডিজাইন করা হয়েছে। এর একটি অংশ রান লুপের শেষে আঁকানো পদক্ষেপগুলি কোলেসেস করছে।
রব নেপিয়ার

1
@ রবনেপিয়ার আপনি বুঝতে পারেন না কেন আপনি এতটা রক্ষণাত্মক হয়ে উঠছেন। আবার চেক করুন; কোনও এআইপিআই লঙ্ঘন নেই (যদিও এটি আপনার পরামর্শের ভিত্তিতে পরিষ্কার হয়ে গেছে আমি যুক্তি দিয়েছি যে নিজের পদ্ধতিটি কল করা লঙ্ঘন নয়), না অচলাবস্থা বা ক্রাশের সম্ভাবনা নেই। এটি একটি "কৌশল "ও নয়; এটি একটি সাধারণ ফ্যাশনে ক্যালায়ারের সেটনিডস ডিসপ্লে / ডিসপ্লেআইফিনিড ব্যবহার করে। তদুপরি, আমি এখন GLKView / OpenGL এর সমান্তরালভাবে কোয়ার্টজ এর সাথে আঁকতে বেশ কিছুদিন ধরে এটি ব্যবহার করছি; এটি আপনার তালিকাভুক্ত সমাধানের চেয়ে নিরাপদ, স্থিতিশীল এবং দ্রুত প্রমাণিত। আপনি যদি আমাকে বিশ্বাস না করেন, চেষ্টা করুন। আপনার এখানে হারানোর কিছুই নেই।
স্লিপ ডি থম্পসন

52

সেটনিডসডিসপ্লে এবং ড্রআরেক্ট কল করার মধ্যে আমার একটি বড় বিলম্বের সাথে সমস্যা ছিল: (5 সেকেন্ড)। দেখা গেল যে আমি মূল থ্রেডের চেয়ে আলাদা থ্রেডে সেটনিডসডিসপ্লে কল করেছি। এই কলটি মূল থ্রেডে সরানোর পরে বিলম্ব চলে গেল।

আশাকরি এতে কিছু সাহায্য হবে.


এটি অবশ্যই আমার বাগ ছিল। আমি এই ছাপের মধ্যে ছিলাম যে আমি মূল থ্রেডে চলছে, তবে এটি এনএসলগড না হওয়া অবধি ছিল না is আমার চুল টানা থেকে বাঁচানোর জন্য ধন্যবাদ!
মিঃ টি

14

মানি-ব্যাক গ্যারান্টিযুক্ত, রিইনফোর্সড-কংক্রিট-সলিড উপায় একটি দৃষ্টিভঙ্গিকে সিঙ্ক্রোনালি আঁকতে বাধ্য করার জন্য (কলিং কোডটিতে ফিরে যাওয়ার আগে)CALayer আপনার UIViewসাবক্লাসের সাথে ইন্টারঅ্যাকশন কনফিগার করা ।

আপনার ইউআইভিউ সাবক্লাসে একটি displayNow()পদ্ধতি তৈরি করুন যা স্তরটিকে " প্রদর্শনের জন্য কোর্স নির্ধারণ করুন " এবং তারপরে " এটি তৈরি করতে" বলবে :

দ্রুতগতি

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
public func displayNow()
{
    let layer = self.layer
    layer.setNeedsDisplay()
    layer.displayIfNeeded()
}

উদ্দেশ্য গ

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
- (void)displayNow
{
    CALayer *layer = self.layer;
    [layer setNeedsDisplay];
    [layer displayIfNeeded];
}

draw(_: CALayer, in: CGContext)এমন একটি পদ্ধতিও প্রয়োগ করুন যা আপনার ব্যক্তিগত / অভ্যন্তরীণ অঙ্কন পদ্ধতিতে কল করবে (যা প্রতিটি UIViewহিসাবে কাজ করে CALayerDelegate) :

দ্রুতগতি

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
override func draw(_ layer: CALayer, in context: CGContext)
{
    UIGraphicsPushContext(context)
    internalDraw(self.bounds)
    UIGraphicsPopContext()
}

উদ্দেশ্য গ

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    UIGraphicsPushContext(context);
    [self internalDrawWithRect:self.bounds];
    UIGraphicsPopContext();
}

এবং internalDraw(_: CGRect)ব্যর্থ-নিরাপদ পাশাপাশি আপনার কাস্টম পদ্ধতি তৈরি করুন draw(_: CGRect):

দ্রুতগতি

/// Internal drawing method; naming's up to you.
func internalDraw(_ rect: CGRect)
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
override func draw(_ rect: CGRect) {
    internalDraw(rect)
}

উদ্দেশ্য গ

/// Internal drawing method; naming's up to you.
- (void)internalDrawWithRect:(CGRect)rect
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
- (void)drawRect:(CGRect)rect {
    [self internalDrawWithRect:rect];
}

এবং এখনই কেবল কল করুন myView.displayNow()যখনই আপনার সত্যিকারের আঁকার জন্য এটির প্রয়োজন হবে (যেমন CADisplayLinkকলব্যাক থেকে ) । আমাদের displayNow()পদ্ধতি বলব CALayerকরার displayIfNeeded(), যা সিঙ্ক্রোনাস আমাদের ফিরে কল হবে draw(_:,in:)এবং অঙ্কন না internalDraw(_:), কী সরানোর আগে প্রসঙ্গ টানা হচ্ছে সঙ্গে চাক্ষুষ আপডেট।


এই পদ্ধতির @ রবনাপিয়ারের উপরের মতো, তবে কল displayIfNeeded()করার পাশাপাশি এটির সুবিধাও রয়েছে setNeedsDisplay(), যা এটি সিঙ্ক্রোনাস করে।

এটি সম্ভব হয়েছে কারণ এসগুলি ড এর স্তরগুলির CALayerচেয়ে বেশি অঙ্কন কার্যকারিতা প্রকাশ UIViewকরে লেআউটের মধ্যে উচ্চ-কনফিগারযোগ্য অঙ্কনের উদ্দেশ্যে স্পষ্টভাবে নকশা করা হয়েছে এবং (কোকোতে অনেকগুলি জিনিস যেমন) নমনীয়ভাবে ব্যবহারের জন্য ডিজাইন করা হয়েছে ( অভিভাবক শ্রেণি হিসাবে, বা একটি প্রতিনিধি হিসাবে, বা অন্যান্য অঙ্কন সিস্টেমের সেতু হিসাবে, বা কেবল তাদের নিজস্ব)। CALayerDelegateপ্রোটোকলের যথাযথ ব্যবহার এই সমস্ত কিছুই সম্ভব করে তোলে।

এর কনফিগারেশন সম্পর্কে আরও তথ্য কোর অ্যানিমেশন প্রোগ্রামিং গাইডের লেয়ার অবজেক্টস সেটআপ করা বিভাগেCALayer পাওয়া যাবে ।


নোট করুন যে ডকুমেন্টেশনের জন্য drawRect:স্পষ্টভাবে বলা আছে "আপনার নিজেরাই এই পদ্ধতিটি সরাসরি কল করবেন না।" এছাড়াও, ক্যালায়ারের displayস্পষ্টভাবে বলেছে "সরাসরি এই পদ্ধতিটিকে কল করবেন না।" আপনি যদি সিঙ্ক্রোনালি স্তরগুলি contentsসরাসরি আঁকতে চান তবে এই বিধিগুলি লঙ্ঘনের দরকার নেই। আপনি যে contentsকোনও সময় স্তরটি আঁকতে পারেন (এমনকি পটভূমির থ্রেডেও)। তার জন্য দর্শনে কেবল একটি সাবলেয়ার যুক্ত করুন। তবে এটি স্ক্রিনে রাখার চেয়ে পৃথক, যা সঠিক সংমিশ্রণের সময় পর্যন্ত অপেক্ষা করা দরকার।
রব নেপিয়ার

আমি একটি সাবলেয়ার যুক্ত করতে বলছি যেহেতু ডক্সগুলিও ইউআইভিউর স্তরের সাথে সরাসরি জগাখিচির বিষয়ে সতর্ক করে দিয়েছে contents("যদি স্তর অবজেক্টটি কোনও ভিউ অবজেক্টের সাথে আবদ্ধ থাকে তবে আপনার সরাসরি এই সম্পত্তির বিষয়বস্তুগুলি সেট করা এড়ানো উচিত views দর্শন এবং স্তরগুলির মধ্যে আন্তঃসংযোগ সাধারণত ফল দেয়) পরবর্তী আপডেটের সময় এই সম্পত্তিটির বিষয়বস্তুগুলি প্রতিস্থাপনের দৃষ্টিতে "") আমি বিশেষত এই পদ্ধতির সুপারিশ করছি না; অকাল অঙ্কন কর্মক্ষমতা এবং অঙ্কনের গুণমানকে হ্রাস করে। তবে আপনার যদি কোনও কারণে এটির প্রয়োজন হয়, তবে contentsএটি কীভাবে পাবেন is
রব নেপিয়ার

@ রবনেপিয়ার পয়েন্ট drawRect:সরাসরি কল করার সাথে নেওয়া হয়েছে । এই কৌশলটি প্রদর্শন করার প্রয়োজন ছিল না, এবং এটি ঠিক করা হয়েছে।
স্লিপ ডি। থম্পসন

@ রবনেপিয়ার contentsআপনার যে কৌশলটির পরামর্শ দিচ্ছেন ... তাতে মোহনীয় শোনায়। আমি প্রথমে এরকম কিছু চেষ্টা করেছি তবে এটি কাজ করতে পারা যায় না এবং উপরের সমাধানটি ঠিক কম পারফরম্যান্ট না হওয়ার কারণ ছাড়াই অনেক কম কোড বলে খুঁজে পেয়েছি। যাইহোক, যদি আপনার কাছে এই contentsপদ্ধতির জন্য কোনও সমাধান সমাধান থাকে তবে আমি এটি পড়তে আগ্রহী (আপনার এই প্রশ্নের দুটি উত্তর না পাওয়ার কারণ নেই, তাই না?)
স্লিপ ডি। থম্পসন

@ রবনেপিয়ার দ্রষ্টব্য: ক্যালায়ারের সাথে এর কোনও যোগসূত্র নেই display। এটি কেবলমাত্র ইউআইভিউ সাবক্লাসে একটি কাস্টম পাবলিক পদ্ধতি, ঠিক যেমন জিএলকেভিউতে করা হয় (অন্য ইউআইভিউ সাবক্লাস, এবং কেবলমাত্র অ্যাপল-রচিত যা আমি জানি যে এটি এখনই আঁকতে হবে! কার্যকারিতা)।
স্লিপ ডি থম্পসন

5

আমার একই সমস্যা ছিল এবং এসও বা গুগলের সমস্ত সমাধান আমার পক্ষে কার্যকর হয়নি। সাধারণত, setNeedsDisplayকাজ করে, কিন্তু যখন তা হয় না ...
আমি setNeedsDisplayপ্রতিটি সম্ভাব্য থ্রেড এবং স্টাফ থেকে প্রতিটি সম্ভাব্য উপায়ে দেখার কল চেষ্টা করেছি - এখনও সফলতা পাইনি। আমরা জানি, যেমন রব বলেছিল, তা

"এটি পরবর্তী ড্র চক্রে আঁকতে হবে।"

তবে কিছু কারণে এটি এবার আঁকবে না। এবং আমি খুঁজে পেয়েছি এমন একমাত্র সমাধান হ'ল এটির কিছুক্ষণ পরে ম্যানুয়ালি কল করা, কোনও কিছু যাতে ড্রকে বাধা দেয়, যাক:

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 
                                        (int64_t)(0.005 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
    [viewToRefresh setNeedsDisplay];
});

আপনার যদি সত্যিই প্রায়শই পুনরায় চিত্র আঁকার জন্য প্রয়োজন না হয় তবে এটি একটি ভাল সমাধান। অন্যথায়, আপনি যদি কিছু চলমান (ক্রিয়া) স্টাফ করেন তবে সাধারণত কল করার ক্ষেত্রে কোনও সমস্যা নেই setNeedsDisplay

আমি আশা করি এটি সেখানে যেমন হারিয়েছিল, তাকে যেমন সাহায্য করবে তেমনভাবে।


0

আচ্ছা আমি জানি এটি একটি বড় পরিবর্তন হতে পারে এমনকি আপনার প্রকল্পের জন্য উপযুক্ত নাও তবে আপনি ইতিমধ্যে ডেটা না পাওয়া পর্যন্ত আপনি কী ধাক্কা না দেওয়ার বিষয়টি বিবেচনা করেছেন ? এইভাবে আপনাকে কেবল একবার ভিউ আঁকতে হবে এবং ব্যবহারকারীর অভিজ্ঞতাও আরও ভাল হবে - ইতিমধ্যে চাপানো ধাক্কাটি স্থানান্তরিত হবে।

আপনি যেভাবে এটি করেন তা হ'ল UITableView didSelectRowAtIndexPathঅবিচ্ছিন্নভাবে ডেটা চাওয়ার জন্য। আপনি প্রতিক্রিয়াটি একবার পেয়ে গেলে, আপনি নিজেই সেগটি সম্পাদন করেন এবং ডেটাটি আপনার ভিউকন্ট্রোলারে প্রবেশ করেন prepareForSegue। এদিকে আপনি সাধারণ লোডিং সূচকটির জন্য কিছু ক্রিয়াকলাপ সূচক দেখাতে চাইতে পারেন https://github.com/jdg/MBProgressHUD

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.